Getting data in Console but cant display in angular - node.js

This is mine products.component.ts and mine Json response on Node server and Angular server but i am not able to render it on my product.component.html
constructor(private http:Http) {
console.log('Hello fellow user');
this.getProducts();
this.getData();
}
getData(){
return this.http.get(this.apiUrl)
.pipe(map((res:Response)=>res.json()))
// return this.http.get(apiUrl, httpOptions).pipe(
// map(this.extractData),
// catchError(this.handleError));
}
getProducts(){
this.getData().subscribe(data=>{
console.log(data);
this.data=data;
})
}
This is mine html of product component
<p>Products work</p>
<div class="container">
<ng-container *ngFor="let product of data.products">
<h2>{{product.id}}</h2>
</ng-container>
</div>
**This is mine res.json**

First of all you should declare your data as array, as typed array if you have Product class:
data: Product[] = [];
Then I think you are wrong referring you data because into component class you assign
this.data=data;
the in your template you try to access with
data.products
Try changing *ngFor line with this:
<ng-container *ngFor="let product of data">

Related

Angular suscribed observable don't showing information on the view

image of the detail view with console to see the console.log()
I'm having troubles making the Tour Of Heroes Angular tutorial work, i'm in the 6 step of the tutorial, getting the data from a server but instead of getting the data from a simulated data server i have a api with nodejs express and mysql.
The problem cames when i try to show the detail of the hero (fetching one by id), all seems to work but the information don't show on the view.
template:
<div *ngIf="hero">
<h2>{{ hero.name }} Details</h2>
<div>id: {{hero.id}}</div>
<div>
<label for="name">Hero name: </label>
<input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
<button type="button" (click)="goBack()">go back</button>
</div>
component:
ngOnInit(): void {
this.getHero();
}
getHero(){
const id = Number(this.route.snapshot.paramMap.get("id"));
this.heroService.getHero(id).subscribe(hero => {
this.hero = hero;
console.log("hero", hero)
})
}
service:
private heroesUrl = 'http://localhost:3300/api/';
constructor(private MessageService: MessageService, private http: HttpClient) {
}
private log(message: string) {
this.MessageService.add(`HeroService: ${message}`);
}
getHeroes(): Observable<Hero[]>{
this.log('HeroService: fetched heroes');
return this.http.get<Hero[]>(this.heroesUrl);
}
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}${id}`;
return this.http.get<Hero>(url);
}
I don't know what's the problem, im learning angular but the observable is well suscribed, in the attached image you can see in the console that at least the api is working.
you received an array with an unique element, see the [``] in console. So
Or in subscribe your write hero[0]
this.heroService.getHero(id).subscribe(hero => {
this.hero = hero[0];
})
Or in your service return the first element of the array. For this use rxjs/operator map
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}${id}`;
return this.http.get<Hero[]>(url).pipe(
map((res:Hero[])=>res[0])
);
}
See that although you say to Angular that getHero return an Observable<Hero> really you got an Observable<Hero[]>. Yes, when we indicate the return of a function this not make "magically" we get the result, only help us to write the code and the editor advise us about it

Angular : [Object Object]

I'm working on a NodeJS/Angular project and while trying to do a simple CRUD, I'm blocked when I try to get an element by ID.
I would like to retrieve all the info of a "Member" based on its ID and display the info in a table. I manage to get my JSON with the API call but when trying to display it in the table, it doesn't show anything.
My service, with the API call :
public getMember(id: number) {
return new Promise((resolve, reject) => {
this.http.get(this.config.apiServer + `astreintes/member/get/${id}`)
.subscribe((res) => {
resolve(res as Member);
console.log(res);
}, err => {
reject(err);
});
});
}
Result of the console.log: My correct JSON with the info of the member
My component.ts :
public search(){
this.memberService.getMember(this.id).then((data) => {
if(data){
this.member = (data as any).recordset;
console.log("Get member :"+ data);
this.indice = true;
}else{
}},
(error) => {
console.log(error);
}
);
}
Result of the console.log: "Get member : [Object Object]"
For the interface, I just have a dropdown list of all my members, and when I select one and click on the button "Search", it gets the info of my member correctly in the console. Then, I want to display it in my table below. My html code:
<form (submit)='search()' #searchMemberForm="ngForm" class="form-horizontal">
<select [(ngModel)]="id" name="member">
<option *ngFor="let member of membersList"
[value]="member.Id_OnCall_Member">{{member.Oncall_Member_Name}}</option>
</select>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-save btn-primary">Search</button>
</div>
</div>
</form>
<div *ngIf="indice">
<h1 style="text-align: center">
Informations
</h1>
<table class="table table-striped" *ngIf="indice">
<th>ID</th>
<th>Nom</th>
<th>Numéro de téléphone</th>
<th>Statut d'activité</th>
<tr *ngFor="let m of member">
<td>{{m.Id_OnCall_Member}}</td>
<td>{{m.Oncall_Member_Name}}</td>
<td>{{m.OnCall_Member_Phone}}</td>
<td>{{m.OnCall_Member_Status}}</td>
</tr>
</table>
</div>
Thanks for your help !
you used "+" in console.log: console.log("Get member :"+ data);
it means javascript is trying to convert the output to one type (string), but data is object. Below you can find how to get the correct output.
const res = {
memberId: 1
}
console.log(res) // {memberId: 1}
console.log('Member: '+ res); //Member: [object Object]
console.log('Member: ', res); // Member: {memberId: 1}
console.log('Member: '+ JSON.stringify(res)); // Member: {memberId: 1}
thanks for your answers.
My problem wasn't the console.log though, it was just an indication.
My real problem was that it didn't show anything in my table. But I solved it, here's how in case it might help other people.
In my component.ts, I replaced:
this.member = (data as any).recordset;
By this, simply:
this.member = data;

Reactjs frontend gets error when backend runs

I'm using React Js as frontend and node express as backend. When I start the my frontend using npm start it runs without errors but when I start my backend too then it throws this error: TypeError: Cannot read property 'map' of undefined and I could not find any solution for this.
Here's the code where the error is:
import React, { Component } from 'react'
import ApiService from "../../service/ApiService";
import SearchField from "./SearchField";
class ListModuleComponent extends Component {
constructor(props) {
super(props)
this.state = {
modules: [],
message: null
}
this.deleteModule = this.deleteModule.bind(this);
this.editModule = this.editModule.bind(this);
this.addModule = this.addModule.bind(this);
this.reloadModuleList = this.reloadModuleList.bind(this);
}
componentDidMount() {
this.reloadModuleList();
}
reloadModuleList() {
ApiService.fetchModules()
.then((res) => {
this.setState({ modules: res.data.result })
});
}
deleteModule(moduleId) {
ApiService.deleteModule(moduleId)
.then(res => {
this.setState({ message: 'Delete successful.' });
this.setState({ modules: this.state.modules.filter(module => module.id !== moduleId) });
})
}
editModule(id) {
window.localStorage.setItem("moduleId", id);
this.props.history.push('/edit-module');
}
addModule() {
window.localStorage.removeItem("moduleId");
this.props.history.push('/add-module');
}
render() {
return (
<div>
<h2 className="text-center">Modul data</h2>
<button className="btn btn-danger" style={{ width: '100px' }} onClick={() => this.addModule()}>Add</button>
<SearchField />
<table className="table table-striped">
<thead>
<tr>
<th className="hidden">Id</th>
<th>Id</th>
<th>Name</th>
{/* <th>Key</th> */}
<th>Default value</th>
<th>Description</th>
<th>State</th>
</tr>
</thead>
<tbody>
{
this.state.modules.map( // Error throws on this line //
module =>
<tr key={module.id}>
<td>{module.moduleName}</td>
<td>{module.moduleDefaultValue}</td>
<td>{module.description}</td>
<td>{module.isActive}</td>
<td>
<button className="btn btn-success" onClick={() => this.deleteModule(module.id)}> Delete</button>
<button className="btn btn-success" onClick={() => this.editModule(module.id)} style={{ marginLeft: '20px' }}>Edit</button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
);
}
}
export default ListModuleComponent;
So the error only show up if my backend starts and I don't know if it is caused by backend or frontend, only see the error for the React frontend side. I can provide more code if needed.
res.result.data is most likely undefined. You could solve this in 2 ways:
1. Prevent undefined in your state.
You could set your state to an empty array in case res.result.data is undefined like so:
this.setState({ modules: res.data.result || [] });
This will make sure your state always contains an array even if the backend provides no data.
2. Do a null-check before rendering your data.
Check if your this.state.modules holds a value before rendering your content.
<div>
{this.state.modules && this.state.modules.map(module => {
// Code
}}
</div>
The default state for modules is an array, this means that mapping over it is okay, so if it doesnt fetch any data then the component will render just fine.
this.state = {
modules: [],
message: null
}
When the backend has been started the reload modules method is setting the modules to be res.data.result
The error is Cannot read property 'map' of undefined.
Because of this I think res.data.result is undefined.
to fix this check what res is and make sure the thing you are setting in state is the array of data
Seems like a null check would fix the obvious problems here.
{
this.state.modules instanceof Array && this.state.modules.map(
module => { /*...*/ }
)
}
Nevertheless, I strongly assume that the backend has already delivered the data correctly. Also your code has an assumption that the array is always there and I cannot find a place where it has been deleted/false initialized. So I Just assume that the backend returns you an undefined/null value, which would also be covered by this check. So maybe investigate your backend whether it returns a null value.

Can't Edit and Update properties with form Reactjs and MongoDB

So I'm using Nodejs, MongoDB and Reactjs
and I'm trying to Edit properties of projects.
I have multiple projects and when I want to edit properties of one I can't do it. We can access to properties inside inputs, we can see Title and Type but can't even delete, write, he access to properties by its ID but then I can't change it, I guess I have multiple problems here than.
I'll write here my server code, and my Edit/Update project page and a gif with an example when I say that I can't even change anything on inputs.
My server code:
//Render Edit Project Page byId
app.get('/dashboard/project/:id/edit', function(req, res){
let id = req.params.id;
Project.findById(id).exec((err, project) => {
if (err) {
console.log(err);
}
res.json(project);
});
}
//Update Projects Properties byId
app.put('/dashboard/project/:id/edit', function(req, res){
var id = req.params.id;
var project = {
title: req.body.title,
typeOfProduction: req.body.typeOfProduction
};
Project.findByIdAndUpdate(id, project, {new: true},
function(err){
if(err){
console.log(err);
}
res.json(project);
})
};
My React Component Edit Project Page
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import './EditProject.css';
class EditProject extends Component {
constructor(props){
super(props);
this.state = {
//project: {}
title: '',
typeOfProduction: ''
};
}
inputChangedHandler = (event) => {
const updatedProject = event.target.value;
}
componentDidMount() {
// console.log("PROPS " + JSON.stringify(this.props));
const { match: { params } } = this.props;
fetch(`/dashboard/project/${params.id}/edit`)
.then(response => { return response.json()
}).then(project => {
console.log(JSON.stringify(project));
this.setState({
//project: project
title: project.title,
typeOfProduction: project.typeOfProduction
})
})
}
render() {
return (
<div className="EditProject"> EDIT
<form method="POST" action="/dashboard/project/${params.id}/edit?_method=PUT">
<div className="form-group container">
<label className="form--title">Title</label>
<input type="text" className="form-control " value={this.state.title} name="title" ref="title" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container">
<label className="form--title">Type of Production</label>
<input type="text" className="form-control " value={this.state.typeOfProduction} name="typeOfProduction" ref="typeOfProduction" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container button">
<button type="submit" className="btn btn-default" value="Submit" onClcik={() => onsubmit(form)}>Update</button>
</div>
</form>
</div>
);
}
}
export default EditProject;
Erros that I have:
1- DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.
2- Inputs can't change
3- When click "Update" button:
I think your update override the entire object because you forgot the $set operator. This is the operator to change only the atributtes of an object and not the entire object replacing!
Example:
Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
First of all, concerning the deprecation warning, you need to change the method findAndModify (As I do not see it here, I guess you're using it elsewhere, or maybe one of the methods you use is calling it) by one of the suggested methods and change your code accordingly.
Then, you need to learn about React and controlled components : https://reactjs.org/docs/forms.html
You need to set the component's state in your onChange handler, such as :
this.setState({
title: event.target.value // or typeOfProduction, depending on wich element fired the event
});
This is called a controlled component in React.
Concerning the response body you get when clicking on Update button, this is actually what you asked for :
res.json(project);
returns the project variable as a JSON file, which is displayed on your screenshot.
See this question for more information about it : Proper way to return JSON using node or Express
Try replace "value" in input tag with "placeholder"

accessing template params ejs express

I'm creating fleet management application, I've set up for vehicles' makes and models and I've established one-to-many relationship in my database model.
I'm trying to present the vehicle make along with its models, however, it doesn't seem to work, below is the code from my routes file:
router.get("/new",function(req,res){
var selectedMake;
Make.find({},function(err,result){
if(err){
console.log("an error occured while fetching the data");
}else{
if(req.query.id){
Make.findById(req.query.id,function(err,foundMake){
console.log("found the query string");
selectedMake = foundMake;
console.log(selectedMake);
})
}
res.render("vehicles/newvehiclemake",{makes:result,selected: selectedMake});
}
})
});
and here is the code where i'm trying to access variable "selected" in my .ejs file
<div class="row">
<div class="col-md-9 col-md-offset-3">
<table class="table table-striped">
<tr>
<th>Available Models</th>
</tr>
<% if(selected) { %>
<% selected.models.forEach(function(model){ %>
<tr><td><%= model.name %></td></tr>
<% }) %>
<% }else { %>
<tr><td>No Models available for the selected Make</td></tr>
<% } %>
</table>
</div>
</div>
the branch where selected should be executed is never reached and always i get No Models available for the selected Make
any clues?
I think that is because your Make.findById method is a asynchronous call. So your callback function(err,foundMake) is called after the res.render
Move your render call into the callback function, then it should work.
if(req.query.id){
Make.findById(req.query.id,function(err,foundMake){
console.log("found the query string");
selectedMake = foundMake;
// after the findById call finished, now it has value.
console.log(selectedMake);
// res.render should be called at this moment.
res.render("vehicles/newvehiclemake",{makes:result,selected: selectedMake});
})
// you would see this line is called before the data is ready.
console.log(selectedMake);
}

Resources