Search code examples
angulartypescriptservice

Angular Outside Subscribe Value Null


I work on a project that basically calls service method via a mediate class which is called ServiceHandler. In the component the console output of the result variable is undefined. Is there any solution to solve this problem?

Component

this.entityServiceHandler.saveEntityWithCondition(entity).subscribe(data => { this.result = data});

console.log(this.result);

ServiceHandler

public saveEntityWithCondition(entity): Observable<Entity>{
    var subject = new Subject<any>();

    this.subscriptions.push(
      forkJoin({
        conditionTrue: this.entityService.isConditionTrue(entity.property)
      }).subscribe(({ conditionTrue }) => {
   
        if (conditionTrue) {
        
            this.entityService.save(entity).subscribe(
              (response: Entity) => {
                subject.next(response);
                
              },
              (errorResponse: HttpErrorResponse) => {
                
              }
            )
        }
      },
      err => {},
      () => {
      }));

    return subject.asObservable();

}

Service

public save(item: Entity) : Observable<Entity>{

  public save(item: Entity): Observable<Entity> {   
      return this.httpClient.post<Entity>(`path`, item).pipe(catchError((err) => this.handleAndThrowError(err)));
    }  
}

Solution

  • The reason why you see undefined is that you are mixing synchronous and asynchronous code and this often leads to problems. Just move the console.log inside the subscribe and it should be OK.

    // Asynchronous code - takes some time to finalize
    this.entityServiceHandler.saveEntityWithCondition(entity).subscribe(data => {
      this.result = data;
      console.log(this.resul) // This'll be shown
    });
    
    // Synchronous code - happens just after calling previous, doesn't wait for it
    // to finish. This means, result is undefined
    console.log(this.result);
    

    EDIT: I took a liberty and simplified your code a bit. You are doing lots of unnecessary things, like forkJoin for just one observable. Or pushing a subscription to an Array, likely to close it later, but it's not needed when you refcator your code a bit.

    import { filter, switchMap, catchError, EMPTY } from 'rxjs';
    
    public saveEntityWithCondition(entity): Observable<Entity> {
      // No need to forkJoin, just call the function and pipe it
      return this.entityService.isConditionTrue(entity.property).pipe(
        // This makes sure that conditionTrue is true indeed
        filter(conditionTrue => conditionTrue),
        // Then use switchMap to call the entity service and perform save
        switchMap(() => this.entityService.save(entity)),
        // And use catchError to handle possible issues
        catchError(error => {
          // handle error in some way - i.e. log it or something;
          return EMPTY; 
        })
      );
    
    }
    

    You don't need subject anymore because you are returning the Observable<Entity> directly.

    You also don't need to push the Subscription to an array, because thanks to the HttpClient you are using and the switchMap operator included, it will handle unsubcribe automatically, when save is finished.