I want to implement a connexion senario based on Roles in angular. When the user click on CONNEXION button after filling in the form ( username - password), two API urls are called to serve the same connexion and I'm using angular 13 for the frontend. In the backend I have the two endpoints ( ressources request url ) to serve the connexion process. The first ressource
this.http.post(
${environment.baseUrlUserSecurity}/users/login
, data, Option)
provides the token for authentication and the second ressource
this.http.get(
${environment.baseUrlUserSecurity}/prsnls
, this.userId)
has to find the user's role based on the userId provided by the firt API call from the token. Everything works fine when testing with postman. But I'm struggling to implement the connexion process in the frontend with angular and this is what I have done so far:
//LOGIN METHOD THAT CALLS THE 2 API URLs
login(data){
const Option ={ headers: new HttpHeaders({'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json',"USER_CODE":data.USER_CODE})};
this.http.post<any>(`${environment.baseUrlUserSecurity}/users/login`, data, Option)
.pipe(
map(value => value[0]))
.subscribe( value => {
this.userId = value.result.userId;
this.http.get(`${environment.baseUrlUserSecurity}/prsnls`, this.userId)
.subscribe( user => {
this.user = user;
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
return user;
});
})
}
//THE ONSUBMIT ACTION WHEN THE BUTTON IS PRESSED
onSubmit(){
const data = this.loginForm.getRawValue();
this.submitted = true;
localStorage.setItem('USER_CODE',data.USER_CODE);
this.loading = true;
this.isLoading = true;
of(null)
.pipe(
delay(2000),
first()
)
.subscribe(() => {
this.isLoading = false;
this.authService.login(data).subscribe(
(res:any)=>{
if (res.code==200){
localStorage.setItem('token',res.result.token);
localStorage.setItem("infos", JSON.stringify(res.result.roleIds));
localStorage.setItem("prsl", res.result.userId);
//this.getUserInfos(res.result.userId);
this.router.navigate(['/dashboard']);
}
this.rMessage = '';
},
error => {
this.rMessage = 'bError';
this.loading = false;
}
);
});
}
The problem is that the editor says the 'subscribe()' property does not exist on type void inside the onsubmit() function on the line this.authService.login(data).subscribe()
and I don't understand why I can't subscribe to that observable. I appreciate if anyone can help, because I don't know how to fix this and get the editor without errors. Thanks in advance.
CodePudding user response:
Remember: "your services return observables, you subscribe in components"
Each time you write "subscribe" in your service you're falling in a bad practice (*)
Well, you need return an observable based in another one, so you need use switchMap rxjs operator. switchMap is always in the way
observable1(data).pipe(switchMap(res=>{
//here res is the response of the observable1(data)
//you return another observable based in res
return observable2(res)
})
Your login
login(data){
const Option ={ headers: new HttpHeaders({'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json',"USER_CODE":data.USER_CODE})};
//see that you "return" an observable
return this.http.post<any>(`${environment.baseUrlUserSecurity}/users/login`, data, Option)
.pipe(
switchMap(value=>{
const userId=value[0].result.userId
return this.http.get(`${environment.baseUrlUserSecurity}/prsnls`, userId)
})
Now you can subscribe
onSubmit(){
const data = this.loginForm.getRawValue();
this.submitted = true;
this.authService.login(data).subscribe(
(res:any)=>{
if (res.code==200){
localStorage.setItem('token',res.result.token);
localStorage.setItem("infos", JSON.stringify(res.result.roleIds));
localStorage.setItem("prsl", res.result.userId);
//this.getUserInfos(res.result.userId);
this.router.navigate(['/dashboard']);
}
this.rMessage = '';
},
error => {
this.rMessage = 'bError';
this.loading = false;
}
);
});
(*)well you can to have in your service some like
subject=new Subject<any>()
getData(){
this.httpClient.get(...).subscribe(res=>{
this.subject.next(res)
})
}
And you subscribe to the "subject" and call to the function
this.authService.subject.subscribe(res=>{
...
})
this.authService.getData();
But it's for some "special cases"
CodePudding user response:
You need to return Observable
from login method.
If you subscribe and return then will be returning Subcription
type.So in-order to return Observable
and manipulate result and do some stuff after response came you have to use pipe
.
I changed your login method like below
login(data): Observable<any> {
const Option = {
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
USER_CODE: data.USER_CODE,
}),
};
return this.http
.post<any>('${environment.baseUrlUserSecurity}/users/login', data, Option)
.pipe(
map((value) => value[0]),
tap((value) => (this.userId = value.result.userId)),
switchMap((value) =>
this.http.get(
'${environment.baseUrlUserSecurity}/prsnls',
this.userId
)
).pipe(
tap((user) => {
this.user = user;
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
}),
map((user) => ({ ...user, ...value }))
)
);
}