Angular 9 can't make a post request - javascript

I'm currentry just a begginner in Angular,
I'm trying make a post request using this function:
signIn(user: User) {
console.log("1");
return this.http
.post<any>(`${this.endpoint}/login`, user)
.subscribe((res: any) => {
console.log("2");
localStorage.setItem("access_token", res.token);
this.getUserProfile(res._id).subscribe(res => {
this.currentUser = res;
this.router.navigate(["user-profile/" + res.msg._id]);
});
});
}
I tried to monitor the network (using the network tab in firefox), and I've found out that no data is sent.
When opening the console it displays "1" but not "2".
Thank you

You need to return the observable and subscribe to it there. Now you are returning a subscription so you might not know when actually the call is triggered. Also try to avoid nested subscriptions. You could use RxJS higher order operators (like switchMap) to pipe multiple observables. Try the following
some service
signIn(user: User) : Observable<any> {
return this.http.post<any>(`${this.endpoint}/login`, user).pipe(
switchMap((user) => {
localStorage.setItem("access_token", user.token);
return this.getUserProfile(user._id);
}
);
}
component
ngOnInit() {
this.someService.signIn(user).subscribe(
res => {
this.currentUser = res;
this.router.navigate(["user-profile/" + res.msg._id]);
},
(error) => {
// always good practice to handle HTTP observable errors
}
);
}

Related

ReactJS - Javascript try/catch shows an error in the console

I'm pretty new on React and I'm learning this language nowadays.
For this purpose I'm building a test project on which I try to encounter the main classical issues and looking to solve it.
For most of React developpers, there are no difficulties in following code but I will give a few details for better comprenhension.
I have a portion of javascript code that is returning a list of articles from a Symfony Backend API only if user is authorized for getting it (Authorization via JWT will be done later). A getArticles function returns a Promise that tries to get the articles from the Symfony backend inside a try {} catch (error) {} block.
Voluntarily, Authorization token is not send to trigger an error in the query.
As the axios.get is located inside a try {} catch (error) {} block, I am surprised that an error appears in the console for the request. It doesn't impact the behavior but it is not very clean to have these errors in the console.
My question(s) :
Why an error appears in the console while the code is inside a try/catch ? To get a cleaner app behavior, is there a way to avoid having this error in the console ? I have found other React try/catch issues but I didn't deduct the similarity with my issue. Am I missing something ?
Thanks in advance ;-)
I am aware that my code could be refactored, do not hesitate to suggest any good practice
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => { return new Promise( (resolve, reject)=> {
try{
const data = axios.get('https://xxxxx/api/articles');
resolve(data);
} catch (err) {
reject(err);
}
});
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}
You can try in this way and Async functions itself returns a promise, you don't need to return a new Promise manually.
async componentDidMount() {
try {
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => req);
const getArticles = async () => {
try {
const data = axios.get('https://xxxxx/api/articles');
this.setState({ errorOnArticlesLoading: false, articles: data.data.items });
} catch (err) {
this.setState( {errorOnArticlesLoading:true} );
}
};
await getArticles()
} catch(err) {
console.log('Handled root error')
}
}
It seems that there are no solutions to avoid the 401 http error code in the console because it it printed by Chrome itself: See discussion here. So the following code cannot avoid the 401 error status to be printed in the console.
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => {
const data = await axios.get('https://xxxx/api/articles');
return data;
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}

How do I ensure I won't replace a content to an old response?

Good day for all,
I am doing a React course and I'd submited the code to the reviewer. He's returned me few comments and there is one comment I'm not being able to solve.
The comment is the following:
Check if (query === this.state.query) to ensure you are not going to replace the contents to an old response
And part of the code is the one below:
updateQuery = (query) => {
this.setState({
query: query
})
this.updateWantedBooks(query);
}
updateWantedBooks = (query) => {
if (query) {
BooksAPI.search(query).then((wantedBooks) => {
if (wantedBooks.error) {
this.setState({ wantedBooks: [] });
} else {
this.setState({ wantedBooks: wantedBooks });
}
})
} else {
this.setState({ wantedBooks: [] });
}
}
Anyone could help me what do am I suppose to do?
Regards.
Code reviewer is right, you don't really want to replace the response if user has entered the very same query.
You have to store somewhere what for user has searched recently:
this.setState({ wantedBooks: [], query });
In case of success response:
this.setState({ wantedBooks, query });
And then check it in case of further searches:
if (query && query !== this.state.query) {
// continue the search only if query is different that current
Instead of relying on an outer member which is open to abuse by other code, you can employ a factory function to more safely trap a member.
As you have discovered, trapping and testing query == this.state.query can be made to work but is arguably not the best solution available.
With a little thought, you can force each call of updateWantedBooks() automatically to reject the previous promise returned by the same function (if it has not already settled), such that any success callbacks chained to the previous promise don't fire its error path is taken.
This can be achieved with a reusable canceller utility that accepts two callbacks and exploits Promise.race(), as follows:
// reusable cancellation factory utility
function canceller(work, successCallback) {
var cancel;
return async function(...args) {
if (cancel) {
cancel(new Error('cancelled')); // cancel previous
}
return Promise.race([
work(...args),
new Promise((_, reject) => { cancel = reject }) // rejectable promise
]).then(successCallback);
};
};
Here's a demo ...
// reusable cancellation factory utility
function canceller(work, successCallback) {
var cancel;
return async function(...args) {
if (cancel) {
cancel(new Error('cancelled')); // cancel previous
}
return Promise.race([
work(...args),
new Promise((_, reject) => { cancel = reject })
]).then(successCallback);
};
};
// delay utility representing an asynchronous process
function delay(ms, val) {
return new Promise(resolve => {
setTimeout(resolve, ms, val);
});
};
function MySpace() {
// establish a canceller method with two callbacks
this.updateWantedBooks = canceller(
// work callback
async (query) => delay(500, query || { 'error': true }), // a contrived piece of work standing in for BooksAPI.search()
// success callback
(wantedBooks => this.setState(wantedBooks)) // this will execute only if work() wins the race against cancellation
);
this.setState = function(val) {
console.log('setState', val);
return val;
};
};
var mySpace = new MySpace();
mySpace.updateWantedBooks({'value':'XXX'}).then(result1 => { console.log('result', result1) }).catch(error => { console.log(error.message) }); // 'cancelled'
mySpace.updateWantedBooks(null).then(result2 => { console.log('result', result2) }).catch(error => { console.log(error.message) }); // 'cancelled'
mySpace.updateWantedBooks({'value':'ZZZ'}).then(result3 => { console.log('result', result3) }).catch(error => { console.log(error.message) }); // {'value':'ZZZ'} (unless something unexpected happened)
Note that canceller() doesn't attempt to abort the asynchronous process it initiates, rather it stymies the success path of the returned promise in favour of the error path.
I think reviewer's point is that response of Search API is asynchronous and result for "query 1" can arrive after user changed his mind and already requested search "query 2". So when response arrive - we need to check if we really interested in it:
updateQuery = query => {
this.setState({
query: query
wantedBooks: []
})
this.updateWantedBooks(query);
}
updateWantedBooks = query => {
if (query) {
BooksAPI.search(query).then((wantedBooks) => {
// if updateQuery("query1) and updateQuery("query2") called in a row
// then response for query1 can arrive after we requested query2
// => for some period of time we'll show incorrect search results
// so adding check if query still the same can help
if (query !== this.state.query) {
// outdated response
return;
} else if (wantedBooks.error) {
// query is okay, but server error in response
this.setState({
wantedBooks: []
})
} else {
// success response to requested query
this.setState({ wantedBooks });
}
})
}
}
Guys I´ve done some tests with your answers, but I realize that somehow the code was behavioring strangely.
So, I've seen in other part of the reviewer comments, a part which I hadn't had seen before do my answer here, the following comment:
Inside 'then' part of the promise check if(query === this.state.query) to ensure you are not going to replace the contents to an old response.
And this "Inside 'then'" has been beating in my brain.
So, I think I've arrived in a satisfatory code; sure, maybe it isn't the definite solution, that's why I want to show here for you and feel free to comment if I'd have to make some improvement. Here below I put the code:
updateQuery = (query) => {
this.setState({
query: query
})
this.updateWantedBooks(query);
}
updateWantedBooks = (query) => {
if (query) {
BooksAPI.search(query).then((wantedBooks) => {
if (wantedBooks.error) {
this.setState({ wantedBooks: [] });
} else if (query !== this.state.query) {
this.setState( { wantedBooks: [] });
} else {
this.setState({ wantedBooks: wantedBooks });
}
})
} else {
this.setState({ wantedBooks: [] });
}
}
Regards

JS: Catch network error and return default response data

I am developing an app that uses promises to communicate with an remote API. This app needs to be able to work offline seamlessly, so I need to handle network errors. Since I can define some default data upfront that is good enough to keep the app functioning. My approach is to catch the error and return a new promise loaded with the default data:
API.js
function getDataFromAPI(id) {
return axios.get(`${BASE_URL}/${id}`)
.then(response => response.data)
.catch((error) => {
// Only return fake data in cases of connection issues
if (error.message == 'Network error') {
const fakeResponse = {myDefaultData: 'default data all over the place'};
// receiving function expects data in promise-form
return Promise.resolve(fakeResponse);
}
});
}
Action.js using the API
needSomeData = () => {
api.getDataFromAPI().then((response) => {
// Data is processed/used here
}));
The code sample works but I am not sure if this is a good/clean approach? Would it be better to handle this in a service worker? Or should I use an entirely different way to approach the issue?
so you can clean it a little bit more.
since any return from .catch consider the value of the next resolved promise. you do not need to return Promise.resolve(value) return value are enough
function getDataFromAPI(id) {
return axios.get(`${BASE_URL}/${id}`)
.then(response => response.data)
.catch((error) => {
// Only return fake data in cases of connection issues
if (error.message == 'Network error') {
return {
myDefaultData: 'default data all over the place'
};
else {
return 'return something or throw new exception'
}
});
}
So for whom that want to know exactly how Promise algorithm behave
Promises/A+ specification
In fact I find It very interesting

Nesting a http request inside list of promises

I have a service function that returns a list of promises:
getData(user) {
return this.$q.all({
userInfo: this.$http.get(this.buildUrl('user.getinfo', user)),
userTopArtists: this.$http.get(this.buildUrl('user.gettopartists', user)),
userChart: this.$http.get(this.buildUrl('user.getWeeklyChartList', user))
}).then(resp => {
return resp;
}).catch(err => {
this.$q.reject('Error' + err.status);
})
}
, which I'm calling inside my controller:
validateUser() {
this.error = null;
this.service.getData(this.$scope.username)
.then(resp => {
if (resp.userInfo.data.user) {
this.service.storeUserData('userData', JSON.stringify(resp));
this.$location.path('/profile');
} else {
this.error = resp.userInfo.data.message;
}
})
}
Works fine until now but what I'm looking for is manipulating what data I get from the userChart request in particular.
I want to manipulate the json I get from calling userChart, store some of it inside of an array and make another request that returns data using the stored array object values from the initial request as parameters.
So basically I don't need the json from userChart, I just need to use to it make a nested(?) request using some of its data.
If you return a promise from the then the caller of the original promise will wait until the nested promise is resolved. It does not matter if the caller was your service using $q.all or something else, it is chained.
This shows just the relevant code, it located in your service with everything else being unchanged.
userChart: this.$http.get(this.buildUrl('user.getWeeklyChartList', user))
.then((result) => {
// additional manipulation if needed on result
// second call to http and return the resulting promise
return this.$http.doSomething('');
});
I haven't tried this, but maybe you could do something with the result of that call as soon as it comes back? Something like this?
getData(user) {
return this.$q.all({
userInfo: this.$http.get(this.buildUrl('user.getinfo', user)),
userTopArtists: this.$http.get(this.buildUrl('user.gettopartists', user)),
userChart: this.$http.get(this.buildUrl('user.getWeeklyChartList', user)).
then(function(response) {
// Do something with the successful response
}, function(response) {
// Do something for the failed response
})
}).then(resp => {
return resp;
}).catch(err => {
this.$q.reject('Error' + err.status);
})
}
If I understood yours needs you should first get the charts response and then use that response to call the other webservices, so something like this should work:
validateUser() {
this.getData({})
.then(function (response) {
// response containing 'userInfo' and 'userTopArtists'
console.log(arguments);
});
}
getData(user) {
const me = this;
return me.$http.get('https://jsonplaceholder.typicode.com/users')
.then(function (charts) {
return me.$q.all({
// use charts as param for other calls...
userInfo: me.$http.get('https://jsonplaceholder.typicode.com/users'),
userTopArtists: me.$http.get('https://jsonplaceholder.typicode.com/users')
});
})
.catch(err => {
me.$q.reject('Error' + err.status);
});
}

Manually throw error ionic 2

I am communicating with a webservice and the return can be negative or porisitvo and will fall into the success of the call, but if the return is negative I need to manually throw an exception
.map(res => res.json())
.subscribe(res => {
let returno = JSON.parse(res.d);
if (returno.status == 'success') {
this._loggedIn(returno);
} else {
throw returno;
}
}, err => {
console.error('ERROR', err);
});
You can throw exceptions in JS by just passing objects to throw. So throw err in your case.
I expect you'll want all of your subscribers to handle the error in the same way? Therefore, you can handle the error at the Observable level, not the Subscriber level.
Thus, you can do something like the following:
public tryLogin(url: string): Observable<any> {
return this.http.get(url)
.map(res => res.json().d)
.map(res => {
if (res.status !== 'success') {
Observable.throw(res);
}
return res;
});
}
Then you can call this code in the following fashion:
this.tryLogin('mydata')
.subscribe(
data => this._loggedIn(data),
err => console.log(err);
)
The great thing with this approach is, if you don't want your subscribers to know what's gone on, you can abstract that and either authenticate them in the next callback, or handle the event of a failure in the error callback.

Categories

Resources