fix(ivy): host listeners being inherited twice (#28902)
Fixes inherited host event listeners being registered twice. This PR resolves FW-1071. PR Close #28902
This commit is contained in:
parent
9dac04ff50
commit
43181ea568
|
@ -728,7 +728,7 @@ const updateBaseDefFromIOProp = (getProp: (baseDef: {inputs?: any, outputs?: any
|
|||
|
||||
const baseDef = constructor.ngBaseDef;
|
||||
const defProp = getProp(baseDef);
|
||||
defProp[name] = args[0];
|
||||
defProp[name] = args[0] || name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -210,6 +210,13 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||
return propMetadata;
|
||||
}
|
||||
|
||||
ownPropMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
if (!isType(typeOrFunc)) {
|
||||
return {};
|
||||
}
|
||||
return this._ownPropMetadata(typeOrFunc, Object) || {};
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
return type instanceof Type && lcProperty in type.prototype;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,8 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
|||
preserveWhitespaces: metadata.preserveWhitespaces || false,
|
||||
styles: metadata.styles || EMPTY_ARRAY,
|
||||
animations: metadata.animations,
|
||||
viewQueries: extractQueriesMetadata(type, getReflect().propMetadata(type), isViewQuery),
|
||||
viewQueries:
|
||||
extractQueriesMetadata(type, getReflect().ownPropMetadata(type), isViewQuery),
|
||||
directives: [],
|
||||
changeDetection: metadata.changeDetection,
|
||||
pipes: new Map(),
|
||||
|
@ -138,7 +139,7 @@ export function extendsDirectlyFromObject(type: Type<any>): boolean {
|
|||
*/
|
||||
function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMetadataFacade {
|
||||
// Reflect inputs and outputs.
|
||||
const propMetadata = getReflect().propMetadata(type);
|
||||
const propMetadata = getReflect().ownPropMetadata(type);
|
||||
|
||||
return {
|
||||
name: type.name,
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Component, HostBinding} from '@angular/core';
|
||||
import {Component, Directive, HostBinding, HostListener, QueryList, ViewChildren} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
|
@ -40,4 +41,60 @@ describe('acceptance integration tests', () => {
|
|||
expect(element.classList.contains('foo')).toBeFalsy();
|
||||
expect(element.classList.contains('bar')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should only call inherited host listeners once', () => {
|
||||
let clicks = 0;
|
||||
|
||||
@Component({template: ''})
|
||||
class ButtonSuperClass {
|
||||
@HostListener('click')
|
||||
clicked() { clicks++; }
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: ''})
|
||||
class ButtonSubClass extends ButtonSuperClass {
|
||||
}
|
||||
|
||||
@Component({template: '<button custom-button></button>'})
|
||||
class MyApp {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyApp, ButtonSuperClass, ButtonSubClass]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
const button = fixture.debugElement.query(By.directive(ButtonSubClass));
|
||||
fixture.detectChanges();
|
||||
|
||||
button.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(clicks).toBe(1);
|
||||
});
|
||||
|
||||
it('should support inherited view queries', () => {
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
}
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
class SuperComp {
|
||||
@ViewChildren(SomeDir) dirs !: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: '<div someDir></div>'})
|
||||
class SubComp extends SuperComp {
|
||||
}
|
||||
|
||||
@Component({template: '<button custom-button></button>'})
|
||||
class MyApp {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyApp, SuperComp, SubComp, SomeDir]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
const subInstance = fixture.debugElement.query(By.directive(SubComp)).componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(subInstance.dirs.length).toBe(1);
|
||||
expect(subInstance.dirs.first).toBeAnInstanceOf(SomeDir);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue