简体   繁体   中英

Angular onpush change detection strategy with primitive values

may be I am misunderstanding the changeDetection strategy in angular.

Here is Description:

  • I have 2 Sibling components (S1 and S2)
  • Think both the components as widget on which some graphical data being displayed
  • When loaded I want to display data on both the component initially. (For this I am using behaviour subject)
  • From S1 I want to notify S2 to trigger a http service call. (I have used behaviour subject and calling .next on click of a button in S1. I am subscribing to same observable in S2 to get the data)
  • My S2 component is registered with onPush change detection strategy.
  • I am using loading text while http service call is in progress and then removing the text after complete using ngIf using primitive variable this.loading=true/false

Now My problem is when App loads initially I can see loading... text on S2 widget and then data gets populated.

But when I click on the notify sibling button in S1, it does triggers the service call but while http is in process I can't see loading... text.

Here is code of S1 :

import { Component, OnInit } from '@angular/core';
import { MessagingService } from '../messaging.service';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  constructor(private _sendMessage: MessagingService) { }

  ngOnInit() {
  }
  notifyChild() {
    this._sendMessage.notifySibling();
  }
}

Here code of S2 :

import { Component, OnInit,ChangeDetectionStrategy ,ChangeDetectorRef} from '@angular/core';
import { MessagingService } from '../messaging.service';
import { switchMap, takeUntil, delay } from 'rxjs/operators';
import { of } from 'rxjs';
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
  public loading = true;
  public myData: any;
  constructor(private _receiveMessage: MessagingService,private cd: ChangeDetectorRef) { }

  ngOnInit() {
    this._receiveMessage.registerNotification().pipe(
      switchMap(res => {
        this.loading = true;
        /** Simulate http call below  */
        return of([res.data]).pipe(
          delay(5000)
        )
      })
    ).subscribe(response => {
      this.myData = response;
      this.loading = false;
      this.cd.markForCheck();
    })
  }

}

Note: I have added this.cd.markForCheck(); in subscription. If i comment this like I am only seeing loading... and no data being displayed

Messaging Service :

import { Injectable } from '@angular/core';
import { Subject, BehaviorSubject } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class MessagingService {
  public _notify = new BehaviorSubject({ data: "initial Call" });
  public notify = this._notify.asObservable();
  constructor() { }

  notifySibling() {
    this._notify.next({ data: 'call from sibling' });
  }
  registerNotification() {
    return this.notify;
  }

}

Full working example is published on stackblitz

OnPush strategy run check only when @Input property is changed if you want to run it in async context you have to call this.cd.markForCheck() yourself before request and after request.

ngOnInit() {
    this._receiveMessage.registerNotification().pipe(
      switchMap(res => {
        this.loading = true;
        /** note this */
        this.cd.markForCheck();
        return of([res.data]).pipe(
          delay(1000)
        )
      })
    ).subscribe(response => {
      this.myData = response;
      this.loading = false;
      this.cd.markForCheck();
    })
  }

https://stackblitz.com/edit/angular-pmvmgz

english ain't my native language :(

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