简体   繁体   中英

Frictions between Declarative and Reactive approach in Angular app

here is an issue I'm facing periodically, when my app is composed of declarative components (so their templates is fully predictable by the value of their attributes) but at some point there's a Subject pushing a new value, in a reactive approach.

Let's say I have something like this: a list of components based on the elements of an array.

<hello *ngFor="let h of helloComponents"></hello>

hello component is subscribed to a BehaviorSubject from a service.

At some point (eg when a button is clicked) the behaviorSubject emits a new value, but also the array is updated.

The order of events is the following:

  1. the subject emits a new value
  2. the components subscribed to the subject receive the new value
  3. the array is updated, the new components are initialized and they receive the new value as the first value from the subject

The problem is that the old components receive the value before they are destroyed, so they may run code I don't want them to execute because they are destroyed at point 3.

If I first update the array and then push a new value from the subject the scenario does not change.

I've found 2 fixes:

A. use a setTimeout to be sure that the subject emits the new value when the old components have been already destroyed by the Angular Change Detection. But I don't know how much robust is this solution...

this.myarray= [4, 5];
setTimeout(() => subject.next(new value));

B. use observeOn(asyncScheduler)

this.stateService.state$.pipe(
        observeOn(asyncScheduler)
    ).subscribe(
      state => console.log(`helloComponent ${this.id}, state: ${state}`)
    )

but I really a newby about Rxjs Scheduler, I don't know if this is a good approach or there's a better alternative.

Here is the stackblitz... open the console

Any ideas?

I used tracking in combination with manual change detection (may look crude, but you're manually updating the helloComponents array in the same event loop tick as triggering update from the service), and it seemed to work:

import { Component, ChangeDetectorRef } from '@angular/core';
import { StateService } from './state.service';

@Component({
  selector: 'my-app',
  template: `<hello *ngFor="let h of helloComponents, trackBy: trackById" [id]="h"></hello> <button (click)="action()">action</button>`
})
export class AppComponent {
  name = 'Angular';
  helloComponents = [1, 2, 3];

constructor(private stateService: StateService, private ref: ChangeDetectorRef) {}

  action() {
    this.helloComponents = [4, 5];
    this.ref.detectChanges();
    this.stateService.action();
  }

  trackById = id => id;
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM