To put my problem simply, I have an element in component's template. This element has an ngIf condition and a (click) handler. It is not rendered from the very beginning, because the ngIf condition evaluates to false.
Now comes the interesting part: A code running outside the angular zone changes that condition to true, and after executing detectChanges on the change detector ref manually, this element gets rendered and the click handler ofc becomes active.
It all seems ok so far, but the problem is that when the (click) callback is run upon user's click, change detection is not triggered for the component.
Here is the reproduction https://stackblitz.com/edit/angular-kea4wi
Steps to reproduce it there:
- Click at the beige area
- Button appears, click it too
- Nothing happens, although message should have appeared below
Description:
The beige area has a click event handler registered via addEventListener, and this event listener's callback is running outside the angular zone. Inside it a component's
showButtonproperty is set fromfalsetotrueand I trigger change detection there manually by callingdetectChanges(), otherwise the change in theshowButtonproperty wouldn't be registered. The code looks like this:this.zone.runOutsideAngular(() => { const el = this.eventTarget.nativeElement as HTMLElement; el.addEventListener('click', e => { this.showButton = true; this.cd.detectChanges(); }) })Now button appears, which thanks to
*ngIf="showButton"wasn't rendered initially, and it has a click even handler declared in the template. This handler again changes component's property, this timeshowMessagetotrue.<button *ngIf="showButton" (click)="onButtonClick()">Click me!</button> onButtonClick() { this.showMessage = true; }When I click it, the handler obviously runs and changes component's
showMessagetotrue, but it doesn't trigger change detection and message below doesn't appear. To make the example work, just set showButton to true from the very beginning, and the scenario above works.
The question is: How is this possible? Since I declared the (click) event handler in the template, shouldn't it always trigger change detection when called?
OnPushso it stops there and does not check further components down the component tree.OnPushhierarchy, and trigger change detection in place - in the component where it bubbled from (DOM-wise). Otherwise it would be a rather common problem and a design flaw in Angular.