Home > front end >  I can not subscribe on mergeMap observable in angular 13
I can not subscribe on mergeMap observable in angular 13

Time:04-22

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 }))
        )
      );
  }
  • Related