14

When we talk about Change Detection mechanism in Angular 2, everyone suggests using NgZone or ChangeDetectorRef.

Is there any working example of application.tick()/ApplicationRef.tick() which also does the same thing as ChangeDetectorRef/NgZone.

Second question: API says, in DEV MODE, ApplicationRef.tick() runs the change detection more than once.

Is there a way to run it only once. Because the moment I use this method, I get an error saying

ApplicationRef.tick() is called recursively

Similar example of the problem I face using tick() is here in this Github Link

I have gone through Triggering Change Detection Manually Question.

Any info in this appreciated. Thanks.

Shashank Vivek
  • 16,888
  • 8
  • 62
  • 104
Aditya Shukla
  • 316
  • 1
  • 3
  • 20

2 Answers2

8

ApplicationRef.tick() calls setTimeout((){})

You should only need this when code that runs outside Angulars zone updates Angulars model and Angular therefore can't get notified about the change by NgZone.

It just invokes change detection for the whole application. Usually it's better wrapping the code that updates the model from the outside with zone.run(() => {...}) than using ApplicationRef.tick()

ChangeDetectorRef.detectChanges() or ChangeDetectorRef.markForCheck() act only on a specific element (and its descendants).

To clarify for a comment below how to get zone:

class MyComponent {
  constructor(private zone:NgZone) {}

  foo() {
    ...
    this.zone....
  }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Günter Zöchbauer - Can you tell me what is the connection between ApplicationRef.tick() and setTimeout(() {}) ? Is it like running setting timeout on change detection mechanism or something else? – Aditya Shukla Oct 17 '17 at 18:06
  • `setTimeout` is recognized by `NgZone` and just invokes normal change detection. – Günter Zöchbauer Oct 17 '17 at 18:07
  • Günter Zöchbauer - Thanks for clearing this out. Can you answer my question too w.r.t to recursion? Will it resolve with setTimeout? – Aditya Shukla Oct 17 '17 at 18:09
  • At first I would need the information why you think you need `ApplicationRef.tick()` in the first place. As I wrote, it's rather uncommon that you need it. If it causes an endless loop, there is probably some other problem in your code. For example if you bind to a function in your components template which will be called every time change detection runs. – Günter Zöchbauer Oct 17 '17 at 18:13
  • 1
    Yes, I am doing the very same thing as you stated in the example. But that's the business requirement. How can I go around in a case like that? – Aditya Shukla Oct 17 '17 at 18:20
  • Actually, I still don't know why you think you need to use `ApplicationRef.tick()` – Günter Zöchbauer Oct 17 '17 at 18:31
  • I have a binding to a function in my component template which is getting called to change the values, every time user clicks on each of the items. Now, this doesn't work without ApplicationRef.tick(). Hence, trying to find a work around to it. – Aditya Shukla Oct 17 '17 at 18:57
  • Binding a function to an event is perfectly fine. What I meant is binding a function to a property/attribute whis is most of the time a very bad idea. You definitely shouldn't need ApplicationRef.tick()` fo code that is called from a `(click)="..."` – Günter Zöchbauer Oct 17 '17 at 19:28
  • 1
    yeah but how do you import zone? you need to provide a full example. I have no way of knowing how to implement this. – tatsu Sep 05 '19 at 12:09
  • `NgZone` is from `@angular/core` and you inject it using the constructor: `constructor(private zone:NgZone) {}` – Günter Zöchbauer Sep 05 '19 at 12:11
  • Instead of providing an example on how to use NgZone, could someone provide an example of how to use app.tick()? – invot Mar 02 '20 at 15:32
  • `markForCheck` will also mark the parents. – Matthieu Riegler Apr 21 '22 at 09:17
6

First of all what is ApplicationRef?

Application Ref contains reference to the root view and can be used to manually run change detection using tick function.

So how do we use the tick() function?

Inject it into your component constructor and use it as you wish!


N.B. I purposely entered the ChangeDetectionStrategy.OnPush to remove the automatic update, so we should force the application to update manually.

In this regard we will use Tick() function !


SIMPLE TEST CASE:

component.ts

@Component ({
   selector: 'my-app',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css'],
   changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
   ...
   counter = 1;

   constructor (private appRef: ApplicationRef) {
     setInterval (() => this.counter ++, 1000);
   }

   updateApplication () {
     this.appRef.tick ();
   }
}

component.html

<h1>{{counter}}</h1>

<button (click)="updateApplication()"> UPDATE APP </button>

In my example every time we click on the UPDATE APP button we call the tick() function which updates every single child component starting from the application root.

So in our case it will simply update the counter variable.

Stackblitz Test Case


As for your second question, if you can avoid having the tick() called twice in devMode the answer is You can't avoid it!

But I don't think this behavior leads to a recursive call, most likely you have used the function incorrectly, grafting components that call the tick() or not disabling the changeDetection so as to start two or more changeDetects together.

Community
  • 1
  • 1
Fmerco
  • 1,160
  • 5
  • 20