Почему пользовательский интерфейс не обновляется, когда я изменяю ссылку на объект через интервал, а обнаружение изменений выполняется принудительно?

#angular

#angular

Вопрос:

Пример:

 @Component({
    selector: "app-ex3-component-a",
    templateUrl: "./ex3-component-a.component.html",
    styleUrls: ["./ex3-component-a.component.scss"],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Ex3ComponentAComponent implements OnInit
{
    public config = {title: "Initial title."};

    public interval: Subscription;

    constructor() { }

    public ngOnInit(): void
    {
    }

    public changeTitle()
    {
        if (this.interval)
        {
            this.interval.unsubscribe();
            this.interval = null;
            return;
        }

        this.interval = interval(300)
        .pipe(
            map(() => ({...this.config, title: `From A (${Math.floor(Math.random() * 1000)})`})),
            tap((newConfig) => this.config = newConfig),
            tap(() => console.log(this.config.title))
        ).subscribe();
    }
}
  

HTML:

 <p>config.title={{config.title}}</p>
<button (click)="changeTitle()">{{interval ? "Stop" : "Start changing"}}</button>
  

Что происходит:

  • каждые 300 мс в консоли отображается новое значение
  • пользовательский интерфейс изменяется только при остановке интервала
  • если я установлю интервал, например, равным 3 секундам, произойдет то же самое

Я ожидаю, что пользовательский интерфейс будет обновлен так же, как и консоль, но этого не происходит.

Ответ №1:

Это потому, что при использовании onPush angular ищет изменения в Input параметрах.

Подробнее: onpush-change-detection-how-it-works

Вы можете обнаружить изменения вручную (это рекомендуемый способ) следующим образом:

 constructor(private cdr: ChangeDetectorRef)

this.interval = interval(300)
        .pipe(
            map(() => ({...this.config, title: `From A (${Math.floor(Math.random() * 1000)})`}))
        ).subscribe(newConfig => {
             this.config = newConfig;
             this.cdr.detectChanges();
             console.log(this.config.title);
         });
  
  • Имейте в виду, что код не тестировался, возможно, в нем есть некоторые опечатки, но основная идея верна.

Комментарии:

1. зависимость «ChangeDetectorRef» от @angular / core, спасибо за ваш быстрый ответ!