How does the change detection cycle work?
Zone monkey patches all of the async events, and when they finish, Zone will notify Angular and then Angular knows it's time to detect the changes (update the view based on the latest model changes), but when you're component is OnPush is not going to detect the changes, unless there has special things happened inside the component, (like a click event, or any of the @input's is changed).
So here is step by step:
Component is initialized, Angular will detect all the changes, your view is updated
You run a setInterval, inside it, you're mutating your model, Angular knows it shouldn't update the view because it's OnPush
The first interval's callback gets called, we're inside the first function, you're marking the component to be deliberately checked, and we’re at the end of the interval function (still the first one interval).
Zone notify's Anguar that an Async event is just finished, time to detect the changes
Angular looks at the OnPush and wants to ignore it, but remembers that you've marked the component to be checked by force
the view gets updated
We go to the second interval and so on.
constructor(private ref: ChangeDetectorRef) {
setInterval(() => {
this.numberOfTicks++;
// the following is required, otherwise the view will not be updated
this.ref.markForCheck();
detectChangesFromZoneJS(); // this is a psudo code, but you can imagine something like this will happen, which is patched by NGZone
// Note that this is the last function that will always be called by Zone at the end of all the async events
}, 1000);
}