485

I'm writing an Angular component that has a property Mode(): string.

I would like to be able to set this property programmatically not in response to any event.

The problem is that in the absence of a browser event, a template binding {{Mode}} doesn't update.

Is there a way to trigger this change detection manually?

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
jz87
  • 9,199
  • 10
  • 37
  • 42

4 Answers4

746

Try one of these:

  • ApplicationRef.tick() - similar to AngularJS's $rootScope.$digest() -- i.e., check the full component tree
  • NgZone.run(callback) - similar to $rootScope.$apply(callback) -- i.e., evaluate the callback function inside the Angular zone. I think, but I'm not sure, that this ends up checking the full component tree after executing the callback function.
  • ChangeDetectorRef.detectChanges() - similar to $scope.$digest() -- i.e., check only this component and its children

You can inject ApplicationRef, NgZone, or ChangeDetectorRef into your component.

Lazar Ljubenović
  • 18,976
  • 10
  • 56
  • 91
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 1
    Thanks, I opted for the 3rd solution so as to not check everything, since the changes are fairly localized. I should investigate the other options when I have more time. Are there any performance implications with each choice? – jz87 Jan 16 '16 at 21:13
  • 44
    +1 for `ChangeDetectorRef.detectChanges()`. validators were firing before my directive could update an input's value. – ps2goat Apr 28 '16 at 22:34
  • is `ApplicationRef.tick()` working nowadays with RC3 ?. – Aral Roca Jun 29 '16 at 12:24
  • I'm on rc4 now and none of these work anymore. The objects do not contain ```tick()```, ```run()``` and ```detectChanges()``` anymore – Guntram Sep 08 '16 at 15:03
  • @Guntram, I'm using the first and third in an rc4 app of mine. My guess is you have typings / installation of the libraries messed up? – JKillian Sep 08 '16 at 22:17
  • hmmm... I could access these objects, but they did not contain these methods. Maybe there are some libs messed up, as I switched from rc4 to rc5 and rolled back and also from rc4 to rc6 and rolled back - because the updates probably switched too much stuff around. I solved our current problem differently now, but we will need to use "manual" events at some point. (I'll keep this post bookmarked ;) ) – Guntram Sep 09 '16 at 13:32
  • Thank you! I was able to fix this issue in a select component by using `changeDetectionRef.detectChanges()`, see commit https://github.com/tb/angular2-mdl-ext/commit/ad8358b3816f8c38fc24f3ccbd302404286679f1 – tomaszbak Sep 20 '16 at 22:10
  • 7
    ApplicationRef.tick() and ChangeDetectorRef.detectChanges() are still present in 2.0.0 final. – Max Mumford Sep 22 '16 at 17:09
  • 59
    Just thought I'd mention this. These are not static methods, they're instance methods. You're going to need to inject these classes as services. – Stephen Paul Oct 06 '16 at 14:17
  • @Mark NgZone.run(callback) OR ChangeDetectorRef.detectChanges() which is better in performance? – Ajey Mar 23 '17 at 09:34
  • 1
    ApplicationRef.tick() helped solve an issue with FireFox v17 (<20) – Dan J Apr 04 '17 at 15:34
  • 1
    Hi Mark , I've asked a similar question and got one more option than those 3. I think that there's one more solution .http://stackoverflow.com/a/43457771/859154 ( add it so your answer will help others) – Royi Namir Apr 17 '17 at 19:16
  • Any way to schedule a function to run on the next digest cycle? Similar to `$timeout(fn, 0, true)` – Bruno Finger May 04 '18 at 11:57
  • `ApplicationRef.tick()` helped me also inside global ErrorHandler. On network errors, UI got stuck. I hide the progress spinner overlay and show a user friendly error message, but the changes were not displayed in UI unless I interacted with the browser window. After adding `tick`, my error handler started working properly. I guess, after a `throw` occurs somewhere in the code, Angular aborts processing changes for a moment and needs a nudge to resume. – JustAMartin Sep 02 '19 at 09:08
  • In Angular 8 you need to create a reference to the ApplicationRef in the constructor for it to work. That being said, neither it nor detectChanges() is helping in my situation where I am changing the active menu item inside a subscription but it doesn't actually goto that menu until you do something else like click a checkbox. Maddening because it works perfectly fine in the same component outside the subscription. – MattE Dec 13 '19 at 13:24
161

I used accepted answer reference and would like to put an example, since Angular 2 documentation is very very hard to read, I hope this is easier:

  1. Import NgZone:

    import { Component, NgZone } from '@angular/core';
    
  2. Add it to your class constructor

    constructor(public zone: NgZone, ...args){}
    
  3. Run code with zone.run:

    this.zone.run(() => this.donations = donations)
    
sites
  • 21,417
  • 17
  • 87
  • 146
  • 12
    where should you put the `zone.run` code and what exactly is `donations`? – suku Mar 03 '17 at 14:29
  • 4
    @suku donations is any property you want to update, so it could be this.foo = bar. zone.run goes wherever you want to update stuff. – Matthew Optional Meehan Jun 01 '17 at 03:16
  • 6
    I used this solution to force a view update when using document.addEventListener("resume", callback) – marcovtwout Nov 30 '17 at 10:34
  • As mentioned by @marcovtwout, as of today, this is the only trick I found to work inside `chrome.runtime.onMessage.addListener` when using Angular to build a Chrome Extension. – Salem Ouerdani Aug 02 '22 at 12:00
  • This worked for me where I am creating a component programmatically and showing it in a dialog box. The dialog box would pop-up but was empty until clicked. In my case the displayed component template has an `*ngIf ` on the top-most element and so, for some reason, needed a cd cycle. – Ken Hadden Jun 21 '23 at 19:11
110

I was able to update it with markForCheck()

Import ChangeDetectorRef

import { ChangeDetectorRef } from '@angular/core';

Inject and instantiate it

constructor(private ref: ChangeDetectorRef) { 
}

Finally mark change detection to take place

this.ref.markForCheck();

Here's an example where markForCheck() works and detectChanges() don't.

https://plnkr.co/edit/RfJwHqEVJcMU9ku9XNE7?p=preview

EDIT: This example doesn't portray the problem anymore :( I believe it might be running a newer Angular version where it's fixed.

(Press STOP/RUN to run it again)

Nuno Tomas
  • 1,101
  • 1
  • 7
  • 7
  • Goo point for detectChanges() not working. I had noticed the same issue and found that it works if the change detection is no OnPush. Would be good to get an explanation for that... – Christian Mar 13 '17 at 22:10
  • 2
    It seems like detectChanges is actually working in this plunker? Am I missing something? – Jared_C Apr 02 '17 at 14:01
  • 1
    You should note that ChangeDetectorRef only works in Components – kevinius May 31 '17 at 12:20
  • (!) This is a bad example, I am sorry. Example does not show anything. First item just get overwritten. Just adjust timers a bit ... – Peter Stegnar Aug 10 '17 at 09:59
  • This example doesn't portrays the problem anymore :( I believe it might be running a newer Angular version where it's fixed. – Nuno Tomas Aug 25 '17 at 16:24
  • The distinction does still exist and is probably related to this https://github.com/juleskremer/angular/commit/f894dbdd78cf463ed53b6c50d883326ff7fbff87 (when using projected content). So it is still possible to demonstrate this but in a different way. – Simon_Weaver Aug 21 '18 at 10:16
  • Just my uneducated result: With `changeDetection: hangeDetectionStrategy.OnPush `, I had to use `detectChanges()` – Nikita Fuchs Apr 15 '20 at 12:28
7

In Angular 2+, try the @Input decorator

It allows for some nice property binding between parent and child components.

First create a global variable in the parent to hold the object/property that will be passed to the child.

Next create a global variable in the child to hold the object/property passed from the parent.

Then in the parent html, where the child template is used, add square brackets notation with the name of the child variable, then set it equal to the name of the parent variable. Example:

<child-component-template [childVariable] = parentVariable>
</child-component-template>

Finally, where the child property is defined in the child component, add the Input decorator:

@Input()
public childVariable: any

When your parent variable is updated, it should pass the updates to the child component, which will update its html.

Also, to trigger a function in the child component, take a look at ngOnChanges.

goWithSwagger
  • 1,557
  • 1
  • 17
  • 19
  • 5
    NO...thats the problem...if you update in the parent...say for exmaple is updated "by reference" in a static method the child wont pick it up as change detection hasnt occured – Bhail Apr 23 '18 at 16:36
  • I have got the same problem for days now. I try to retrieve data from PHP backend and data does not Update at all. On Microsoft Edge works fine but on other browser it does not. I keep getting the data i first got on start but if data changes it does not get updated on view – VisDesign Jul 14 '19 at 16:58