In my html front, the user can fill in the detail product information. When click on the button to save the data, the onSubmit function will be started. Here is my example:
onSubmit() {
this.projectService.createProject().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
});
this.projectService.createProjectDetails().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
})
}
in the projectService:
createProject(): Observable<any> {
return this.http.post(PROJECT_URL + "createProject", httpOptions);
}
createProjectDetails(): Observable<any> {
return this.http.post(PROJECT_URL + "createProjectDetails", httpOptions);
}
I dont show all the code but my question is, when there is an error in the createProject, the second function still will run while I dont want this. Can I put the createProjectDetails in the next of the createProject?
Since you seem to work on an Angular-app, you could use switchMap (from RxJs) in order to chain both request. In this case the second function will not run if the first one results in an error:
onSubmit() {
this.projectService.createProject().pipe(
switchMap(() => this.projectService.createProjectDetails())
).subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
});
}
Alternatively you could use the catchError() operator instead of the error callback.
onSubmit() {
this.projectService.createProject().pipe(
switchMap(() => this.projectService.createProjectDetails()),
catchError((err) => {
console.log(err);
return throwError(err);
})
).subscribe((data) => {
console.log(data);
});
}
please create a new function and fire that function when the response is success.
onSubmit() {
this.projectService.createProject().subscribe({
next: data => {
console.log(data);
this.createProjectDetails(); // fire function only here
},
error: err => {
console.log(err);
}
});
}
createProjectDetails(){
this.projectService.createProjectDetails().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
})
}
In the above example, createProjectDetails() will get fired once createProject() is complete. I hope this solves your question, let me know if there are any issues.
Try this using rxjs
import { mergeMap } from 'rxjs/operators'
When we need data from the first API request to make requests to the second API.
onSubmit() {
this.projectService.createProject().pipe(
map(project = {
console.log(project);
return project;
}),
mergeMap( project => this.projectService.createProjectDetails(project.id))
).subscribe( projDetails = {
console.log(projDetails)
});
}
subscribe is a common way to handle requests in Angular, but there are more effective methods. We will first solve our problem using subscribe and then improve on it using mergeMap.
Related
I have Angular 7-8 project back end is Node.JS, I could not found a solution on how to check if my record inserted or updated successfully in database.
here is my service.ts file
UpdateData(data, id) {
try {
return this.http.post(this.baseurl + "/update/" + id, data)
.subscribe(result => console.log(result));
} catch (err) {
console.log("-------Error occurred update posting----" + err);
this.handleError(err);
}
}
I want to check in my component.ts if the insert/update completed successfully or not after calling this service function.
This can be done within the subscribe method like below:
return this.http.post(this.baseurl + "/update/" + id, data)
.subscribe(result => console.log(result),
(error) => {
// error logic here
},
() => {
// request success logic here
});
However if you want to check for the error/success in your component you shouldn't subscribe in your service and perform the actual subscribe in your component like below:
service:
return this.http.post(this.baseurl + "/update/" + id, data);
In your component.ts
this.serviceInstance.UpdateData(data, id).subscribe(result => console.log(result),
(error) => { /* error logic */ }, () => { /* success logic */})
this.http.post(this.baseurl + "/update/" + id, data).subscribe(result => {
if (result.status == "SUCCESS") {
console.log(result)
} else {
this.dataService.toster('Please try again later: somthing went wrong', "error")
}
}, err => {
this.dataService.toster('Please try again later: somthing went wrong', "error")
})
}
Dataservice.ts
import { BehaviorSubject } from 'rxjs';
export class dataService {
public tosterData = new BehaviorSubject<any>([]);
toster(data, status) {
const obj = {
message: data,
status: status
}
this.tosterData.next(obj);
}
class MyService {
public UpdateData(data, id) {
return this.http.post(this.baseurl + "/update/" + id, data);
}
}
then
Subscribe error callback
class MyComponent {
public InsertRecord(data, id) {
this.myService.UpdateData(data, id).subscribe({
next: result => {
// show success
},
error: err => {
// show fail
}
});
}
}
-- or --
async await to use try catch
class MyComponent {
public InsertRecord(data, id) {
try {
const result = await this.myService.UpdateData(data, id).toPromise();
// show success
} catch (err) {
// show fail
}
}
}
What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch
How can I make two service calls in the OnInit() method of the component ?
export class ApartmentComponent implements OnInit {
public apartments: Object[];
public temp: Object[];
constructor(private apartmentService: ApartmentService) {
this.apartmentService = apartmentService;
}
ngOnInit() {
this.apartmentService.getApartments().subscribe(res => this.apartments = res);
this.apartmentService.getStats().subscribe(res => this.temp = res);
console.log(JSON.stringify(this.temp));
}
}
In service.ts
getApartments() {
return this.http.get('./api/businessunits/butype').map((res: Response) => res.json());
}
getStats(){
console.log('Request reached');
return this.http.get('./api/apartments/getstats').map((res: Response) => res.json());
}
in server.ts (ExpressJS)
router.route('/api/businessunits/butype')
.get(function(req, res) {
BusinessUnit.find({unitID: {$exists: true}, UnitType: {$exists: true}},'unitID UnitType',{sort:{unitID: 1}},function(err, businessunits) {
if (err)
res.send(err);
res.json(businessunits);
});
});
router.route('/api/apartments/getstats')
.get(function(req, res) {
//Apartment.aggregate([{$match:{_id: "aptType"}},{$group:{_id:{aptType:"$aptType"},count:{$sum:1}}}],function(err, apartments) {
Apartment.find('aptType',function(err, apartments) {
if (err)
res.send(err);
res.json(apartments);
});
});
The getApartments() works fine individually when I comment out getStats() method call.
I am getting the following error
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (M:\workspace\Angular2StartKit\node_modules\express
Subscribing to observables is an async operation, that means this just schedules a tasks to be done later.
When console.log(JSON.stringify(this.temp) is executed, the call to the server in getStats() (if it actually makes one - I just assume it does) wasn't even sent, and therefor definitely no response received yet.
It is also not clear from the code in your question whether the request for getApartments() or getStats() is sent first.
To preserve a specific order in async operations, you need to chain them properly so that the next is executed when the former is completed.
If you just want to print the result of getStats() this can be done like
ngOnInit() {
this.apartmentService.getApartments().subscribe(res => this.apartments = res);
this.apartmentService.getStats().subscribe(res => {
this.temp = res;
JSON.stringify(this.temp)
});
}
alternatives are
ngOnInit() {
this.apartmentService.getApartments().subscribe(res => this.apartments = res);
this.apartmentService.getStats()
.map(res => this.temp = res);
.subscribe(temp => console.log(JSON.stringify(this.temp));
});
}
or
ngOnInit() {
this.apartmentService.getApartments().subscribe(res => this.apartments = res);
this.apartmentService.getStats()
.map(res => this.temp = res);
.toPromise().then(temp => console.log(JSON.stringify(this.temp));
});
}
If you want to chain 2 subscribes
this.apartmentService.getApartments().subscribe(res => this.apartments = res);
this.apartmentService.getStats().subscribe(res => this.temp = res);
there are lots of possiblilities like flatMap() depending on your requirements. You might want that they are sent one after the other is completed, or send both as soon as possible but then wait for both to complete. There are different ways to handle errors, ...
For more details see http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html
I have tried everything and can't figure out what i am doing wrong. I have no problem posting data from the client to the server but the other way around i can't get it to work.
The only response i get in my client is ReadableByteStream {}.
This is my code on the client:
export function getAllQuestionnairesAction(){
return (dispatch, getState) => {
dispatch(getAllQuestionnairesRequest());
return fetch(API_ENDPOINT_QUESTIONNAIRE)
.then(res => {
if (res.ok) {
console.log(res.body)
return dispatch(getAllQuestionnairesSuccess(res.body));
} else {
throw new Error("Oops! Something went wrong");
}
})
.catch(ex => {
return dispatch(getAllQuestionnairesFailure());
});
};
}
This is my code on the server:
exports.all = function(req, res) {
var allQuestionnaires = [];
Questionnaire.find({}).exec(function(err, questionnaires) {
if(!err) {
console.log(questionnaires)
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ a: 1 }));
//res.json(questionnaires)
}else {
console.log('Error in first query');
res.status(400).send(err);
}
});
}
I'm doing some guesswork here, since I'm not sure what flavor of fetch you are currently using, but I'll take a stab at it based on the standard implementation of fetch.
The response inside the resolution of fetch typically does not have a directly readable .body. See here for some straight forward examples.
Try this:
export function getAllQuestionnairesAction(){
return (dispatch, getState) => {
dispatch(getAllQuestionnairesRequest());
return fetch(API_ENDPOINT_QUESTIONNAIRE)
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error("Oops! Something went wrong");
}
})
.then(json => {
console.log(json); // response body here
return dispatch(getAllQuestionnairesSuccess(json));
})
.catch(ex => {
return dispatch(getAllQuestionnairesFailure());
});
};
}
Currently I developing web app for nodejs using popular sequelize orm and typesciprt. Here is a example from my code
this.createNewGame(player.idPlayer).then((game) => {
this.getBestScore(player.idPlayer).then((bestScore) => {
defer.resolve({
idGame: game.idGame,
bestScore: bestScore
});
}).catch((error) => { defer.reject(error); });
}).catch((error) => { defer.reject(error); });
Here is one of the method
private getBestScore(idPlayer: number): Q.Promise<number> {
var defer = this.q.defer<number>();
GameModel.max<number>('score', { where: { 'idPlayer': idPlayer } }).then((result) => {
defer.resolve(result);
}).catch((error) => { defer.reject(error); });
return defer.promise;
}
I use catch in every method implementation and also in every call to method. I would like to have only one catch block in my expressjs router. I tried code like this and it works just fine, here is example:
//code in GameService class ...
getData(): Q.Promise<number> {
var defer = this.q.defer<number>();
this.method1().then((result) => {
defer.resolve(result);
});
return defer.promise;
}
private method1(): Q.Promise<number> {
var defer = this.q.defer<number>();
throw 'some error occurs here';
return defer.promise;
}
//router call GameService
router.get('/error-test', (req: express.Request, res: express.Response) => {
gameService.getData().then((result) => {
res.json(result);
}).catch((error) => { res.send(error); });
//works fine, here I get my thrown error
});
But in my previous example I need to use catch blocks everywhere otherwise If I get Unhandled rejection SequelizeDatabaseError or any other Unhandled rejection, nodejs stops working. Why I can't use only one catch block in my expressjs router when using calls to db with sequalize, like in my first example?
Sequelize operations return promises, so there is no reason to put Q into the mix. This is equivalent
private getBestScore(idPlayer: number): Q.Promise<number> {
return GameModel.max<number>('score', { where: { 'idPlayer': idPlayer } });
}
It doesn't return a Q.Promise (sequelize uses bluebird), but the implementations should be interoperable, since they are both 'thenables'.