@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
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.
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.
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.
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));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With