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
showButton
property is set fromfalse
totrue
and I trigger change detection there manually by callingdetectChanges()
, otherwise the change in theshowButton
property 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 timeshowMessage
totrue
.<button *ngIf="showButton" (click)="onButtonClick()">Click me!</button> onButtonClick() { this.showMessage = true; }
When I click it, the handler obviously runs and changes component's
showMessage
totrue
, 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?
OnPush
so it stops there and does not check further components down the component tree. – alt255OnPush
hierarchy, 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. – Dan Macák