I am trying to develop a simple crud app using MEAN stack, but when trying to post form value angular service is not making post request. get request works perfectly but post not. i am using morgan to see the requests made to the nodejs server and it is not receiving the post request and all seems to be the same way that in the angular docs. I imported HttpClient modules.
empleados.component.html
<form [formGroup]="nuevoEmpleado" (submit)="addEmpleado()">
<label>
Nombre:
<input type="text" formControlName="nombre">
</label>
<label>
Apellido:
<input type="text" formControlName="apellido">
</label>
<label>
Cargo:
<input type="text" formControlName="cargo">
</label>
<button class="btn btn-primary" type="submit">Agregar Nuevo Empleado</button>
</form>
{{ nuevoEmpleado.value | json }}
<div class="main-container">
<table class="table table-dark">
<thead>
<tr>
<th scope="col">Id</th>
<th scope="col">Nombre</th>
<th scope="col">Apellido</th>
<th scope="col">Cargo</th>
</tr>
</thead>
<tbody>
<tr *ngFor = "let empleado of listaEmpleados">
<th>{{ empleado.index }}</th>
<td>{{ empleado.nombre }}</td>
<td>{{ empleado.apellido}}</td>
<td>{{ empleado.cargo }}</td>
</tr>
</tbody>
</table>
</div>
empleados.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup , FormControl } from '#angular/forms';
import { EmpleadosService } from './empleados.service'
import { Empleado } from '../../clases/empleado'
#Component({
selector: 'app-empleados',
templateUrl: './empleados.component.html',
styleUrls: ['./empleados.component.css']
})
export class EmpleadosComponent implements OnInit {
listaEmpleados: Empleado[];
nuevoEmpleado = new FormGroup({
nombre: new FormControl(''),
apellido: new FormControl(''),
cargo: new FormControl('')
});
constructor(private empleadosService: EmpleadosService) { }
getEmpleados(){
this.empleadosService.getEmpleados().subscribe(empleados => this.listaEmpleados = empleados);
}
addEmpleado(){
this.empleadosService.addEmpleado(this.nuevoEmpleado.value);
}
ngOnInit(): void {
this.getEmpleados();
this.addEmpleado();
}
}
empleados.service.ts
import { Injectable } from '#angular/core';
import { HttpClient , HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Empleado } from '../../clases/empleado'
#Injectable({
providedIn: 'root'
})
export class EmpleadosService {
//url = 'assets/config.json'
url = 'http://localhost:3000'
constructor(private http: HttpClient) { }
getEmpleados():Observable<Empleado[]>{
return this.http.get<Empleado[]>(this.url);
}
addEmpleado(nuevoEmpleado: Empleado){
console.log(nuevoEmpleado);
return this.http.post<Empleado>(this.url , nuevoEmpleado)
}
}
Expanding on the comment made by R. Richards, without a subscription on the post call the Observerable never fires.
An Observerable is simply a function definition (sort of), it requires a subscription to it to actually get the call to run.
An observable itself is just a definition of a way to handle a stream of values. We can think of it as something close to a function. Creating an observable is somewhat similar to declaring a function, the function itself is just a declaration. Calling the function is something entirely different, as defining a function does not execute its code.
We need to call the function in order for something to happen, and that is also the case with an Observable: we need to subscribe to it in order for it to work!
Taken from 3 common Angular Rxjs Pitfalls
Look at the examples on that link to see what you're missing.
Observables are lazy, untill you subscribe them they wont be fullfilled where in case of promises this can work.
So you will need to subscribe to your post call observable as you have done in get api call.
You must subscribe, since when making an http call that returns an observable, remember that observables are lazy by nature.
Related
app.component.ts
Debugger given the error that this.task is undefined
updateTodo(task: any){
this.todoService.updateData(task._id, this.task).subscribe(res => {
this.data= res;
console.log(res);
console.log(this.task);
});
}
app.service.ts
This is service file where the backend api are call in my angular app
updateData(id: any , data: any){
return this.httpClient.put('http://localhost:3000/todos/'+id, data);
}
app.component.html
This is the frontend of my app where the todos show and others user interface
<tbody>
<tr *ngFor="let todo of tasks ; let i = index">
<td>{{todo.todos}}</td>
<td> <input type="checkbox" (change)="updateTodo(todo)"[checked]="todo.isDone</td>
<td>
<button class="btn btn-danger btn-sm" id="del-btn"
(click)="deleteData(todo._id)">Delete</button>
</td>
</tr>
</tbody>
app.model.ts
This is model file
export class Todo {
_id:any;
todos:any;
isDone:any;
}
backend api
This is the backedn function which i created to update my todo
router.put('/:id' , async (req,res) => {
const id = req.params.id;
if(ObjectId.isValid(id)){
const todo = (req.body);
const todoUpdate = await Todo.findByIdAndUpdate(id ,{$set:emp}, {new:true});
res.status(200).json({code:200, message:'Todo Updated Successfully'});
}
else{
res.status(400).send('Todo Not Found By Given Id' + id);
}
});
I'm not sure if we understood each other, but you are passing the task as a parameter but then on two occasions you are trying to use the value of this.task. They are not the same thing and if this.task is not initialized then of course it will show that it's undefined.
updateTodo(task: any) {
console.log('task:', task); // Is the task correct?
this.todoService.updateData(task._id, task).subscribe(res => {
this.data = res;
console.log(res);
console.log(task); //not this.task
});
}
EDIT:
If the DB is not updated you might be sending incorrect data there. If there are no errors on Angular side you have to check the Back-End side.
I solve this question to add [(ngModel)]="todo.isDone" in my checkbox input filed
<tbody>
<tr *ngFor="let todo of tasks ; let i = index">
<td>{{todo.todos}}</td>
<td> <input type="checkbox" (change)="updateTodo(todo)" [(ngModel)]="todo.isDone</td>
<td>
<button class="btn btn-danger btn-sm" id="del-btn"
(click)="deleteData(todo._id)">Delete</button>
</td>
</tr>
And In my app.component.ts
updateTodo(task: any) {
this.todoService.updateData(task._id, task).subscribe(res => {
this.data = res;
console.log(res);
console.log(task);
});
}
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.
I have the code of my component like :
import React, { Component } from 'react';
import axios from 'axios';
import "bootstrap/dist/css/bootstrap.min.css";
import {
Table
} from 'reactstrap';
const Adhoc = async (props) => {
let cost = await props.cost(props.adhoc._id)
return(
<tr>
<td>{props.adhoc.jIssue}</td>
<td>{props.adhoc.paid ? "Paid" : "Not paid"}</td>
<td>APP{props.adhoc.sprint}</td>
<td>£{cost.data[0]}</td>
</tr>
)}
export default class QAdhocsDisplay extends Component {
constructor(props) {
super(props);
this.costingAdhoc = this.costingAdhoc.bind(this)
this.state = {
adhocs: []
};
}
componentDidMount() {
axios.get('http://localhost:5000/adhocs/retrieve')
.then(response => {
this.setState({ adhocs: response.data })
})
.catch((error) => {
console.log(error);
})
}
async costingAdhoc(id) {
const data = await axios.get('http://localhost:5000/jira/issue/' + id)
.catch((error) => {
console.log(error);
})
return data;
}
adhocsList() {
return this.state.adhocs.map(currentadhoc=> {
return <Adhoc adhoc={currentadhoc} cost={this.costingAdhoc} key={currentadhoc._id}/>;
})
}
render(){
return (
<div className="toborder" style = {{paddingBottom: "59px"}}>
<div className="display" style ={{backgroundColor: "#5394b2"}}>
<h5 style = {{padding:"13px"}}>Adhoc status</h5>
</div>
<div className="table">
<Table size="sm" bordered striped>
<thead className="thead-light">
<tr className="adhocs">
<th className="sticky-column medium" >Adhoc issue</th>
<th className="sticky-column medium" >Payment status</th>
<th className="sticky-column medium" >Sprint</th>
<th className="sticky-column medium" >Projected cost</th>
</tr>
</thead>
<tbody>
{ this.adhocsList() }
</tbody>
</Table>
</div>
</div>
)}
}
My issue is that I have the function costingAdhoc(id) which I pass as a prop to the child component Adhoc. To be able to access the information from the axios call I need these both functions to be async.
A child component of type Adhoc will be rendered for each item in the state that will get mapped in the function adhocsList(). For some reason this causes the axios call in the componentDidUpdate() to throw this error:
Objects are not valid as a React child (found: [object Promise]). If
you meant to render a collection of children, use an array instead.
And the error points to the line where I set the state. This means that the costingAdhoc(id) function async nature causes my axios call in the componentDidUpdate() function only return a promise and not the actual data.
Your problem that is Adhoc component which you're declaring as a child component is actually a promise.
Why?
A function that is declared as async returns promise by default, so even if you return:
<tr>
<td>{props.adhoc.jIssue}</td>
<td>{props.adhoc.paid ? "Paid" : "Not paid"}</td>
<td>APP{props.adhoc.sprint}</td>
<td>£{cost.data[0]}</td>
</tr>
you're actually returning it wrapped inside a promise. React components cannot be promises.
I am more familiar with NodeJs than react. I have build a react component that searches for user input and provides the output in a table format based on the value that the user has typed into the search input form. This is working as I want and the code for the module is below:
import React, { Component } from 'react';
import axios from 'axios';
import Suggestions from './Suggestions';
// API url
const API_URL = 'http://localhost:3000/api/file_infos'
class Search extends Component {
state = {
query: '',
results: []
}
getCount = () => {
axios.get(`${API_URL}count?filter[where][id][regexp]=/${this.state.query}/i`)
.then(count => {
this.setState({
results: count.data
})
})
}
// query loop back API for matching queries base on text input
getInfo = () => {
axios.get(`${API_URL}?filter[where][id][regexp]=/${this.state.query}/i&filter[limit]=20`)
.then(response => {
this.setState({
results: response.data
})
})
}
// check to see if input on the search bar has changed and update the search query accordingly
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query) {
this.getInfo()
}
} else if (!this.state.query) {
}
})
}
// render form and pass results back to the home component
render() {
return (
<div>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
</form>
<Suggestions results={this.state.results} />
</div>
)
}
}
export default Search
The second module is the suggestions module that displays the output in the table format.
The next portion of the app I am building will open a file based on the table row that the user selected. I want that table data returned to a function so that I can make an http post request to my API that will in turn open the file using a NodeJS module.
I want the suggestions component to return the value of the data items in the table cells so that the data can be used to send to the API in order to open my files. The code I have come up with so far is only returning an undefined error.
Below is what I currently have:
import React from 'react';
// return results in a table format based on the text input entered
const Suggestions = (props) => {
const state = {
results: []
}
const handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
const options = props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return <table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
}
export default Suggestions;
I am really unsure at this point if I am trying to tackle this issue in the correct way. I am thinking that maybe the suggestions component may need to be turned into a full class extending component but I am fairly lost at this point. Can someone please kindly point out my folly and get me going in the right direction?
UPDATE
As requested in the comments here is the error log from my browser:
Suggestions.js:10 Uncaught TypeError: Cannot read property 'results' of undefined
at Object.handleFormOpen (Suggestions.js:10)
at HTMLUnknownElement.callCallback (react-dom.development.js:145)
at Object.invokeGuardedCallbackDev (react-dom.development.js:195)
at invokeGuardedCallback (react-dom.development.js:248)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:262)
at executeDispatch (react-dom.development.js:593)
at executeDispatchesInOrder (react-dom.development.js:615)
at executeDispatchesAndRelease (react-dom.development.js:713)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:724)
at forEachAccumulated (react-dom.development.js:694)
at runEventsInBatch (react-dom.development.js:855)
at runExtractedEventsInBatch (react-dom.development.js:864)
at handleTopLevel (react-dom.development.js:4857)
at batchedUpdates$1 (react-dom.development.js:17498)
at batchedUpdates (react-dom.development.js:2189)
at dispatchEvent (react-dom.development.js:4936)
at interactiveUpdates$1 (react-dom.development.js:17553)
at interactiveUpdates (react-dom.development.js:2208)
at dispatchInteractiveEvent (react-dom.development.js:4913)
First thing Since your Suggestions component plays with state, I would recommend you to go with statefull component.
Stateless component is meant for getting props and returning jsx elements, there wont be any state mutations in stateless component. This is called pure function in javascript. Hope this makes clear.
Also since you declared handleFormOpen as an arrow function you no need to do binding. binding takes care automatically by arrow function. If you don't want to use arrow function and you want to bind it then do the binding always in constructor only but don't do binding anywhere in the component like you did in map.
PFB corrected Suggestions component code
import React, { Component } from 'react';
// return results in a table format based on the text input entered
export default class Suggestions extends Component {
constructor(props){
super(props);
this.state = {
results: [],
value: ""
}
}
handleFormOpen = (path, id) => {
console.log("id", id, path);//like wise pass value to this function in .map and get the value here
this.setState({
value: id
});
}
render(){
const { results } = this.props;
return (<div>
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{Array.isArray(results) && results.length > 0 && results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={() => this.handleFormOpen(r.OriginalPath, r.id)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))}
</tbody>
</table>
</div>)
}
}
export default Suggestions;
You are using states in Functional Component, You need to use React Component
import React from 'react';
// return results in a table format based on the text input entered
class Suggestions extends React.Component {
constructor(props) {
super(props);
this.state = {
results: [],
}
}
handleFormOpen = () => {
this.setState({
results: this.results.value
},
console.log(this.state.results)
)
}
render () {
const options = this.props.results.map(r => (
<tr key={r.id} ref={tr => this.results = tr} onClick={handleFormOpen.bind(this)}>
<td>{r.id}</td>
<td>{r.OriginalPath}</td>
<td>{r.CreateDate}</td>
<td>{r.AccessDate}</td>
<td>{r.WriteDate}</td>
<td><i className="fas fa-book-open"></i></td>
</tr>
))
return (
<table className="striped responsive-table">
<thead>
<tr>
<th>File Name</th>
<th>Parent Directory</th>
<th>Creation Date</th>
<th>Access Date</th>
<th>Write Date</th>
<th>Open File</th>
</tr>
</thead>
<tbody>
{options}
</tbody>
</table>
)
}
}
export default Suggestions;
I have the following controller:
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from webhelpers.html.tags import HTML
from improve.lib.base import BaseController, render
from improve import model
import improve.model.meta as meta
import improve.lib.helpers as h
log = logging.getLogger(__name__)
class AlarmsController(BaseController):
def list(self):
c.alarms = meta.Session.query(model.Alarms).all()
return render('../public/listalarms_table.html')
rendered by this template (listalarms_table.html):
...
<div id="markup">
<table id="alarms">
<thead>
<tr>
<th>Id</th>
<th>Severity</th>
<th>Node</th>
<th>Count</th>
<th>Last Alarm</th>
<th>Log Msg</th>
<th>AckUser</th>
</tr>
</thead>
<tbody>
% for alarms in c.alarms:
<tr>
<td>${alarms.alarmid.__repr__()|n}</td>
<td>${alarms.severity.__repr__()|n}</td>
<td>${alarms.nodeid.__repr__()|n}</td>
<td>${alarms.counter.__repr__()|n}</td>
<td>${alarms.lasteventtime.__repr__()|n}</td>
<td>${alarms.logmsg.__repr__()|n}</td>
<td>${alarms.alarmackuser.__repr__()|n}</td>
</tr>
% endfor
</tbody>
</table>
</div>
<script type="text/javascript">
YAHOO.util.Event.addListener(window, "load", function() {
YAHOO.example.EnhanceFromMarkup = new function() {
var myColumnDefs = [
{key:"id",label:"Id", sortable:true},
{key:"severity",label:"Severity", sortable:true}
{key:"node",label:"Node", sortable:true}
{key:"count",label:"Count",formatter:YAHOO.widget.DataTable.formatNumber,sortable:true},
{key:"lastalarm",label:"LastAlarm",formatter:YAHOO.widget.DataTable.formatCurrency,sortable:true},
{key:"logmsg",label:"Log Msg"}
];
this.myDataSource = new YAHOO.util.DataSource(YAHOO.util.Dom.get("alarms"));
this.myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
this.myDataSource.responseSchema = {
fields: [{key:"id"},
{key:"severity"},
{key:"node"},
{key:"count", parser:"number"},
{key:"lastalarm", parser:"date"} // point to a custom parser
]
};
this.myDataTable = new YAHOO.widget.DataTable("markup", myColumnDefs, this.myDataSource,
{caption:"Example: Progressively Enhanced Table from Markup",
sortedBy:{key:"id",dir:"asc"}}
);
};
});
The problem:
If I call it using http://myserver:port/listalarms_table.html , I see the correct formating of the YUI table but instead of the data I get the alarms.alarmid.repr()|n instead of the real data
If i call it http://myserver:port/alarms/list I get the data correctly but I loose the YUI format...
How can i get the data with the correct YUI layout?
Thx