1

I am working on a notification component in which I have an array of "error" objects and iterate through them using *ngFor. My issue is that Angular won't pick up the changes to the array when I mutate it inside a subscription.

My code is the following:

  ngOnInit() {
    this.store.select().pipe(
      map(state => state.error),
      filter(error => !!error),
      map(error => ({text: error, display: 'block'}))
    ).subscribe(error => {

      // this does not get detected
      this.errors.push(error);

      setTimeout(() => {
        error.display = 'none';
      }, 4000)
    });

    // this gets detected
    this.errors.push({
      text: 'asd',
      display: 'block'
    })
  }

And the related html:

      <div class="notification-wrapper">
          <div *ngFor="let error of errors" [style.display]="error.display" class="notification is-danger has-text-right">
              {{ error.text }}
          </div>
      </div>

The weird thing is that if I replace the subscription with a setInterval in which I constantly add dummy errors, the changes are caught by Angular and it behaves normally.

Can someone explain to me why it works this way, and maybe how to fix this? Thank you.

9
  • where do you display your errors can you share the html please? Commented Nov 2, 2019 at 1:49
  • Are you with the OnPush strategy ? Commented Nov 2, 2019 at 1:49
  • @Wandrille No, the default one Commented Nov 2, 2019 at 1:51
  • How do you send those errors? Are you sure that your subscription is executed inside Angular zone? Commented Nov 2, 2019 at 1:55
  • @yurzui I have a service that holds a BehaviourSubject which gets returned as an Observable via the select method. I'm not sure what "Angular zone" means, but the code inside the subscription gets executed, I checked. Commented Nov 2, 2019 at 1:58

1 Answer 1

2

Looks like your code is executed outside of Angular zone. You can force to run it inside:

import { NgZone } from '@angular/core';

constructor(private ngZone: NgZone) {}

...

this.store.select().pipe(
  map(state => state.error),
  filter(error => !!error),
  map(error => ({text: error, display: 'block'}))
).subscribe(error => {

  this.ngZone.run(() => {
    this.errors.push(error);

    setTimeout(() => {
      error.display = 'none';
    }, 4000)
  });
});
Sign up to request clarification or add additional context in comments.

1 Comment

Don't do this, use observables with the async pipe.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.