fix(ivy): output should not be subscribe twice when 2 listeners (#30144)
PR Close #30144
This commit is contained in:
parent
f3ce8eeb83
commit
6c86ae710a
|
@ -110,6 +110,8 @@ function listenerInternal(
|
|||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
|
||||
|
||||
let processOutputs = true;
|
||||
|
||||
// add native event listener - applicable to elements only
|
||||
if (tNode.type === TNodeType.Element) {
|
||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||
|
@ -149,6 +151,7 @@ function listenerInternal(
|
|||
// Attach a new listener at the head of the coalesced listeners list.
|
||||
(<any>listenerFn).__ngNextListenerFn__ = (<any>existingListener).__ngNextListenerFn__;
|
||||
(<any>existingListener).__ngNextListenerFn__ = listenerFn;
|
||||
processOutputs = false;
|
||||
} else {
|
||||
// The first argument of `listen` function in Procedural Renderer is:
|
||||
// - either a target name (as a string) in case of global target (window, document, body)
|
||||
|
@ -180,7 +183,7 @@ function listenerInternal(
|
|||
|
||||
const outputs = tNode.outputs;
|
||||
let props: PropertyAliasValue|undefined;
|
||||
if (outputs && (props = outputs[eventName])) {
|
||||
if (processOutputs && outputs && (props = outputs[eventName])) {
|
||||
const propsLength = props.length;
|
||||
if (propsLength) {
|
||||
const lCleanup = getCleanup(lView);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Directive, ErrorHandler, HostListener, QueryList, ViewChildren} from '@angular/core';
|
||||
import {Component, Directive, ErrorHandler, EventEmitter, HostListener, Input, Output, QueryList, ViewChild, ViewChildren} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
@ -203,5 +203,40 @@ describe('event listeners', () => {
|
|||
expect(returnsFalseDir.event.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not subscribe twice to the output when there are 2 coalesced listeners', () => {
|
||||
@Directive({selector: '[foo]'})
|
||||
class FooDirective {
|
||||
@Input('foo') model: any;
|
||||
@Output('fooChange') update = new EventEmitter();
|
||||
|
||||
updateValue(value: any) { this.update.emit(value); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'test-component',
|
||||
template: `<div [(foo)]="someValue" (fooChange)="fooChange($event)"></div>`
|
||||
})
|
||||
class TestComponent {
|
||||
count = 0;
|
||||
someValue = -1;
|
||||
|
||||
@ViewChild(FooDirective) fooDirective: FooDirective|null = null;
|
||||
|
||||
fooChange() { this.count++; }
|
||||
|
||||
triggerUpdate(value: any) { this.fooDirective !.updateValue(value); }
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]});
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const componentInstance = fixture.componentInstance;
|
||||
componentInstance.triggerUpdate(42);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(componentInstance.count).toEqual(1);
|
||||
expect(componentInstance.someValue).toEqual(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue