fix(ivy): host bindings and listeners not being inherited from undecorated classes (#30158)

Fixes `HostBinding` and `HostListener` declarations not being inherited from base classes that don't have an Angular decorator.

This PR resolves FW-1275.

PR Close #30158
This commit is contained in:
Kristiyan Kostadinov
2019-04-27 09:33:10 +02:00
committed by Andrew Kushnir
parent 164d160b22
commit 68ff2cc323
15 changed files with 365 additions and 117 deletions

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, R3BaseRefMetaData, compileBaseDefFromMetadata} from '@angular/compiler';
import {ConstantPool, R3BaseRefMetaData, compileBaseDefFromMetadata, makeBindingParser} from '@angular/compiler';
import {PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, ClassMember, Decorator, ReflectionHost} from '../../reflection';
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
import {queriesFromFields} from './directive';
import {extractHostBindings, queriesFromFields} from './directive';
import {isAngularDecorator} from './util';
function containsNgTopLevelDecorator(decorators: Decorator[] | null, isCore: boolean): boolean {
@ -69,6 +69,12 @@ export class BaseDefDecoratorHandler implements
result = result || {};
const queries = result.queries = result.queries || [];
queries.push({member: property, decorators});
} else if (
isAngularDecorator(decorator, 'HostBinding', this.isCore) ||
isAngularDecorator(decorator, 'HostListener', this.isCore)) {
result = result || {};
const host = result.host = result.host || [];
host.push(property);
}
}
});
@ -85,7 +91,8 @@ export class BaseDefDecoratorHandler implements
analyze(node: ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
AnalysisOutput<R3BaseRefMetaData> {
const analysis: R3BaseRefMetaData = {};
const analysis: R3BaseRefMetaData = {name: node.name.text, typeSourceSpan: null !};
if (metadata.inputs) {
const inputs = analysis.inputs = {} as{[key: string]: string | [string, string]};
metadata.inputs.forEach(({decorator, property}) => {
@ -133,12 +140,17 @@ export class BaseDefDecoratorHandler implements
analysis.queries = queriesFromFields(metadata.queries, this.reflector, this.evaluator);
}
if (metadata.host) {
analysis.host = extractHostBindings(
metadata.host, this.evaluator, this.isCore ? undefined : '@angular/core');
}
return {analysis};
}
compile(node: ClassDeclaration, analysis: R3BaseRefMetaData, pool: ConstantPool):
CompileResult[]|CompileResult {
const {expression, type} = compileBaseDefFromMetadata(analysis, pool);
const {expression, type} = compileBaseDefFromMetadata(analysis, pool, makeBindingParser());
return {
name: 'ngBaseDef',
@ -149,8 +161,9 @@ export class BaseDefDecoratorHandler implements
}
export interface R3BaseRefDecoratorDetection {
inputs?: Array<{property: ClassMember, decorator: Decorator}>;
outputs?: Array<{property: ClassMember, decorator: Decorator}>;
inputs?: {property: ClassMember, decorator: Decorator}[];
outputs?: {property: ClassMember, decorator: Decorator}[];
viewQueries?: {member: ClassMember, decorators: Decorator[]}[];
queries?: {member: ClassMember, decorators: Decorator[]}[];
host?: ClassMember[];
}

View File

@ -194,7 +194,7 @@ export function extractDirectiveMetadata(
throw new Error(`Directive ${clazz.name.text} has no selector, please add it!`);
}
const host = extractHostBindings(directive, decoratedElements, evaluator, coreModule);
const host = extractHostBindings(decoratedElements, evaluator, coreModule, directive);
const providers: Expression|null =
directive.has('providers') ? new WrappedNodeExpr(directive.get('providers') !) : null;
@ -460,11 +460,11 @@ type StringMap<T> = {
[key: string]: T;
};
function extractHostBindings(
metadata: Map<string, ts.Expression>, members: ClassMember[], evaluator: PartialEvaluator,
coreModule: string | undefined): ParsedHostBindings {
export function extractHostBindings(
members: ClassMember[], evaluator: PartialEvaluator, coreModule: string | undefined,
metadata?: Map<string, ts.Expression>): ParsedHostBindings {
let hostMetadata: StringMap<string|Expression> = {};
if (metadata.has('host')) {
if (metadata && metadata.has('host')) {
const expr = metadata.get('host') !;
const hostMetaMap = evaluator.evaluate(expr);
if (!(hostMetaMap instanceof Map)) {
@ -501,7 +501,7 @@ function extractHostBindings(
throw new FatalDiagnosticError(
// TODO: provide more granular diagnostic and output specific host expression that triggered
// an error instead of the whole host object
ErrorCode.HOST_BINDING_PARSE_ERROR, metadata.get('host') !,
ErrorCode.HOST_BINDING_PARSE_ERROR, metadata !.get('host') !,
errors.map((error: ParseError) => error.msg).join('\n'));
}