fix(ivy): listeners inherited twice if sub class has own propMetadata (#29353)

Fixes host listeners being inherited twice, if the sub class has its own `propMetadata`. This is related to #29170 which fixed something similar, however all of the test cases there had a super class with some metadata and a sub class that didn't have any. The issue manifested itself in the `MatTreeToggle` which inherits a listener from the `CdkTreeToggle` and adds an extra `Input` of its own, causing the listener to be added twice.

PR Close #29353
This commit is contained in:
Kristiyan Kostadinov
2019-03-16 15:30:36 +01:00
committed by Jason Aden
parent e8df000e97
commit 3d5b98631a
2 changed files with 37 additions and 3 deletions

View File

@ -27,8 +27,17 @@ export function setClassMetadata(
type: Type<any>, decorators: any[] | null, ctorParameters: (() => any[]) | null,
propDecorators: {[field: string]: any} | null): void {
const clazz = type as TypeWithMetadata;
// We determine whether a class has its own metadata by taking the metadata from the parent
// constructor and checking whether it's the same as the subclass metadata below. We can't use
// `hasOwnProperty` here because it doesn't work correctly in IE10 for static fields that are
// defined by TS. See https://github.com/angular/angular/pull/28439#issuecomment-459349218.
const parentPrototype = clazz.prototype ? Object.getPrototypeOf(clazz.prototype) : null;
const parentConstructor: TypeWithMetadata|null = parentPrototype && parentPrototype.constructor;
if (decorators !== null) {
if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) {
if (clazz.decorators !== undefined &&
(!parentConstructor || parentConstructor.decorators !== clazz.decorators)) {
clazz.decorators.push(...decorators);
} else {
clazz.decorators = decorators;
@ -45,7 +54,8 @@ export function setClassMetadata(
// decorator types. Decorators on individual fields are not merged, as it's also incredibly
// unlikely that a field will be decorated both with an Angular decorator and a non-Angular
// decorator that's also been downleveled.
if (clazz.propDecorators !== undefined) {
if (clazz.propDecorators !== undefined &&
(!parentConstructor || parentConstructor.propDecorators !== clazz.propDecorators)) {
clazz.propDecorators = {...clazz.propDecorators, ...propDecorators};
} else {
clazz.propDecorators = propDecorators;