Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Child component's view is not re-rendered when using OnPush strategy

@Component({
    selector: "parent",
    template: `<child [userId]="(userId$ | async)"></child>`,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentCmp implements OnInit {
    userId$: BehaviorSubject<string> = new BehaviorSubject<string>(null);

    constructor(private activatedRoute: ActivatedRoute) { }
    ngOnInit() {
        this.activatedRoute.queryParams.subscribe(query => {
            //notify child with BehaviorSubject
            this.userId$.next(query["userid"])
        }
    }
}

@Component({
    selector: "child",
    template: `<div *ngIf="(userState$ | async) && userId">
                    {{(userState$ | async).user.id)}}
               </div>`,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildCmp implements OnChanges {
    @Input() userId: string;
    private userState$: Observable<User>;

    constructor(private store: Store<store.AppState>) { }
    ngOnChanges(changes: SimpleChanges) { 
        //when it gets userId it starts to track fit user in ngrx store
        this.userState$ = this.store
                .select(state => state.user-list)                 
                .map(userList => userList[this.userId])
                .filter(user => !!user);
    }
}

The Child cmp gets userId from Parent one and needed user is contained in ngrx store (userList), but child's view is not re-rendered. It works perfectly when ChangeDetectionStrategy for the Child is default. What can be wrong here? Angular v2.4

like image 497
Ilya Loskutov Avatar asked Aug 06 '17 16:08

Ilya Loskutov


People also ask

What is OnPush strategy?

The OnPush strategy changes Angular's change detection behavior in a similar way as detaching a component does. The change detection doesn't run automatically for every component anymore. Angular instead listens for specific changes and only runs the change detection on a subtree for that component.

In which situations will angular check for changes in a component using the OnPush change detection strategy?

An OnPush change detector gets triggered in a couple of other situations other than changes in component Input() references, it also gets triggered for example: if a component event handler gets triggered. if an observable linked to the template via the async pipe emits a new value.

What is the difference between OnPush and default change detection?

OnPush means that the change detector's mode will be set to CheckOnce during hydration. Default means that the change detector's mode will be set to CheckAlways during hydration.


1 Answers

If you change the model in ngOnChanges() you need to invoke change detection explicitely

export class ChildCmp implements OnChanges {
    @Input() userId: string;
    private userState$: Observable<User>;

    constructor(
      private store: Store<store.AppState>,
      private cdRef:ChangeDetectorRef
    ) { }
    ngOnChanges(changes: SimpleChanges) { 
        //when it gets userId it starts to track fit user in ngrx store
        this.userState$ = this.store
                .select(state => state.user-list)                 
                .map(userList => userList[this.userId])
                .filter(user => !!user);
        this.cdRef.detectChanges();
    }
}

or probably better make userStates$ an Observable and keep the same instance instead of creating a new one each time ngOnChanges is called:

userId$: Subject<User> = new Subject<User>();

ngOnChanges(changes: SimpleChanges) { 
    //when it gets userId it starts to track fit user in ngrx store
    this.store
            .select(state => state.user-list)                 
            .map(userList => userList[this.userId])
            .filter(user => !!user)
            .subscribe((user) => this.userId.next(user));
}
like image 104
Günter Zöchbauer Avatar answered Sep 30 '22 07:09

Günter Zöchbauer