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 usingngIf
using primitive variablethis.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
angular typescript
add a comment |
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 usingngIf
using primitive variablethis.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
angular typescript
add a comment |
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 usingngIf
using primitive variablethis.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
angular typescript
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 usingngIf
using primitive variablethis.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
angular typescript
angular typescript
asked Nov 26 '18 at 7:10
user1608841user1608841
1,85111126
1,85111126
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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 :(
ok so I believe addingthis.cd.markForCheck();
is the only option if i am updating primitive values in async context>
– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess usingthis.cd.markForCheck();
is not hack.. evenasync
pipe behind the scene usesmarkForCheck
so its safe to use
– user1608841
Nov 26 '18 at 10:47
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53476204%2fangular-onpush-change-detection-strategy-with-primitive-values%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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 :(
ok so I believe addingthis.cd.markForCheck();
is the only option if i am updating primitive values in async context>
– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess usingthis.cd.markForCheck();
is not hack.. evenasync
pipe behind the scene usesmarkForCheck
so its safe to use
– user1608841
Nov 26 '18 at 10:47
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
add a comment |
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 :(
ok so I believe addingthis.cd.markForCheck();
is the only option if i am updating primitive values in async context>
– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess usingthis.cd.markForCheck();
is not hack.. evenasync
pipe behind the scene usesmarkForCheck
so its safe to use
– user1608841
Nov 26 '18 at 10:47
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
add a comment |
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 :(
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 :(
answered Nov 26 '18 at 7:45
karoluSkaroluS
618213
618213
ok so I believe addingthis.cd.markForCheck();
is the only option if i am updating primitive values in async context>
– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess usingthis.cd.markForCheck();
is not hack.. evenasync
pipe behind the scene usesmarkForCheck
so its safe to use
– user1608841
Nov 26 '18 at 10:47
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
add a comment |
ok so I believe addingthis.cd.markForCheck();
is the only option if i am updating primitive values in async context>
– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess usingthis.cd.markForCheck();
is not hack.. evenasync
pipe behind the scene usesmarkForCheck
so its safe to use
– user1608841
Nov 26 '18 at 10:47
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
ok so I believe adding
this.cd.markForCheck();
is the only option if i am updating primitive values in async context>– user1608841
Nov 26 '18 at 10:20
ok so I believe adding
this.cd.markForCheck();
is the only option if i am updating primitive values in async context>– user1608841
Nov 26 '18 at 10:20
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
I tried this and was working previously :) ..just wanted to clear if this is the only solution . I Thought I was doing something wrong.
– user1608841
Nov 26 '18 at 10:21
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
i think you could move this request to parent component and pass loading and response to sibling using @Input parameters or just change detectionStrategy to Default
– karoluS
Nov 26 '18 at 10:42
I guess using
this.cd.markForCheck();
is not hack.. even async
pipe behind the scene uses markForCheck
so its safe to use– user1608841
Nov 26 '18 at 10:47
I guess using
this.cd.markForCheck();
is not hack.. even async
pipe behind the scene uses markForCheck
so its safe to use– user1608841
Nov 26 '18 at 10:47
1
1
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
it is safe as long as you know what you are doing :P
– karoluS
Nov 26 '18 at 10:51
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53476204%2fangular-onpush-change-detection-strategy-with-primitive-values%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown