fix(core): Ensure OnPush ancestors are marked dirty when events occur (#39833)
We currently only wrap the event listener in the function which ensures ancestors are marked for check when the listener is placed on an element that has a native method for listening to an event. We actually need to do this wrapping in all cases so that events that are attached to non-rendered template items (`ng-template` and `ng-container`) also mark ancestors for check when they receive the event. fixes #39832 PR Close #39833
This commit is contained in:
parent
e148382bd0
commit
68d4a74411
|
@ -193,6 +193,10 @@ function listenerInternal(
|
||||||
lCleanup.push(listenerFn);
|
lCleanup.push(listenerFn);
|
||||||
tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, useCapture);
|
tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, useCapture);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Even if there is no native listener to add, we still need to wrap the listener so that OnPush
|
||||||
|
// ancestors are marked dirty when an event occurs.
|
||||||
|
listenerFn = wrapListener(tNode, lView, listenerFn, false /** preventDefault */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// subscribe to directive outputs
|
// subscribe to directive outputs
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, DoCheck, EmbeddedViewRef, ErrorHandler, Input, NgModule, OnInit, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
import {ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, DoCheck, EmbeddedViewRef, ErrorHandler, EventEmitter, Input, NgModule, OnInit, Output, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {ivyEnabled} from '@angular/private/testing';
|
import {ivyEnabled} from '@angular/private/testing';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
|
@ -461,6 +461,41 @@ describe('change detection', () => {
|
||||||
expect(comp.doCheckCount).toEqual(2);
|
expect(comp.doCheckCount).toEqual(2);
|
||||||
expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy');
|
expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check parent OnPush components when child directive on a template emits event',
|
||||||
|
fakeAsync(() => {
|
||||||
|
@Directive({
|
||||||
|
selector: '[emitter]',
|
||||||
|
})
|
||||||
|
class Emitter {
|
||||||
|
@Output() event = new EventEmitter<string>();
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.event.emit('new message');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '{{message}} <ng-template emitter (event)="message = $event"></ng-template>',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
class MyApp {
|
||||||
|
message = 'initial message';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = TestBed.configureTestingModule({declarations: [MyApp, Emitter]})
|
||||||
|
.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.nativeElement.textContent.trim()).toEqual('initial message');
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.textContent.trim()).toEqual('new message');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ChangeDetectorRef', () => {
|
describe('ChangeDetectorRef', () => {
|
||||||
|
|
Loading…
Reference in New Issue