Home > Enterprise >  Updating Angular component from a service using BehaviorSubject
Updating Angular component from a service using BehaviorSubject

Time:09-21

I am trying to show/hide an ng-container based on a variable on a service. The value should be changed in a Global Error Handler that should catch all the exceptions on my application.

In my service, I have created a variable that exposes my BehaviorSubject as observable.

export class ErrorsService {

  private notify = new BehaviorSubject(false);

  // Subscribe to cast in the component
  cast = this.notify.asObservable();

  // Change notify value
  notifyError(){
    this.notify.next(true);
  }

  constructor() { }

}

I injected this service in my global error handler in order to call notifyError() as followed:

export class GlobalErrorHandler implements ErrorHandler {

  constructor(private errorService: ErrorsService) { }
  handleError(error: any) {

    // Notify subscribers
    this.errorService.notifyError();
     
    //continue handling the error

    }
  }

In my component, I am using the same service to subscribe to the variable "cast" and change the local variable show.

export class ErrorsBlockComponent implements OnInit {

  show = false;
  constructor(private errorsService:ErrorsService) {}

  ngOnInit(): void {
    this.errorsService.cast.subscribe(value=>{
      this.show = value;
    });
  }
  
}

Finally, my component's html looks like

 <ng-container *ngIf="show">There is an Error!</ng-container>

The view wont update until for instance I click on a button! (any button in the app). The show value is changed to true, but the view seems to not update.

My question is why the view doesn't update when the service value is updated as expected? When I use console.log(value) I can see the value becoming true, but nothing happens on the UI until I press a button.


Update:

I replicated the issue on StackBlitz

https://stackblitz.com/edit/angular-ivy-j7thwx?file=src/app/app.component.ts

I also used ChangeDetectorRef.detectChanges() which seemed to fix the problem. However, I would like to know why it didn't work without it since this project is for learning purposes.

CodePudding user response:

This is because your code runs outside the angular Zone. If anything happens outside angular zone you need to run NgZone to push your change.

Here is how you can check whether your code line is within NgZone.

NgZone.isInAngularZone()

In your case you can do

  ngOnInit() {
    console.log('Outside subscription Is in angular zone: '   NgZone.isInAngularZone());
    // Subscribe to error service
    this.errorsService.cast.subscribe((value) => {
      this.show = value;
      console.log('Within subscription Is in angular zone: '   NgZone.isInAngularZone());

    });

    // The getSomething call is going to throw an error
    this.anotherService
      .getSomthing()
      .subscribe((result) => (this.result = result));
  }

Checkout the updated stackblitz

To trigger the change you can to

this.ngZone.run( () => {
     this.show = value;
  });

As stated in here

CodePudding user response:

public notify = new BehaviorSubject<boolean>(false);
notifyError(val){
    this.notify.next(val)
}

And you can subscribe

this.errorsService.notify.subscribe(res=>{
    this.error=res
}

you can try this once , You haven't done wrong , didn't see much why it didn't work

  • Related