2016-06-23 16:47:54 +00:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2016-09-13 02:14:17 +00:00
|
|
|
import {Component, Directive, HostBinding, HostListener, Injectable, Input, Output, Query, Type, resolveForwardRef} from '@angular/core';
|
2016-08-31 01:07:40 +00:00
|
|
|
|
2016-07-11 23:01:49 +00:00
|
|
|
import {StringMapWrapper} from './facade/collection';
|
2016-09-27 22:41:37 +00:00
|
|
|
import {stringify} from './facade/lang';
|
2016-08-31 01:07:40 +00:00
|
|
|
import {ReflectorReader, reflector} from './private_import_core';
|
2016-07-11 23:01:49 +00:00
|
|
|
import {splitAtColon} from './util';
|
2014-11-12 01:33:47 +00:00
|
|
|
|
2015-09-20 01:39:35 +00:00
|
|
|
/*
|
2016-09-13 02:14:17 +00:00
|
|
|
* Resolve a `Type` for {@link Directive}.
|
2015-07-07 06:15:58 +00:00
|
|
|
*
|
|
|
|
|
* This interface can be overridden by the application developer to create custom behavior.
|
|
|
|
|
*
|
|
|
|
|
* See {@link Compiler}
|
|
|
|
|
*/
|
2015-03-16 21:44:14 +00:00
|
|
|
@Injectable()
|
2015-05-12 00:59:39 +00:00
|
|
|
export class DirectiveResolver {
|
2016-06-17 17:57:50 +00:00
|
|
|
constructor(private _reflector: ReflectorReader = reflector) {}
|
2016-03-24 20:32:47 +00:00
|
|
|
|
2015-07-07 06:15:58 +00:00
|
|
|
/**
|
2016-09-13 02:14:17 +00:00
|
|
|
* Return {@link Directive} for a given `Type`.
|
2015-07-07 06:15:58 +00:00
|
|
|
*/
|
2016-09-13 02:14:17 +00:00
|
|
|
resolve(type: Type<any>, throwIfNotFound = true): Directive {
|
2016-09-27 22:41:37 +00:00
|
|
|
const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
|
|
|
|
if (typeMetadata) {
|
|
|
|
|
const metadata = typeMetadata.find(isDirectiveMetadata);
|
|
|
|
|
if (metadata) {
|
|
|
|
|
const propertyMetadata = this._reflector.propMetadata(type);
|
2015-11-04 09:08:51 +00:00
|
|
|
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
2014-11-12 01:33:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-09-27 22:41:37 +00:00
|
|
|
|
2016-07-18 10:50:31 +00:00
|
|
|
if (throwIfNotFound) {
|
2016-08-25 07:50:16 +00:00
|
|
|
throw new Error(`No Directive annotation found on ${stringify(type)}`);
|
2016-07-18 10:50:31 +00:00
|
|
|
}
|
2016-09-27 22:41:37 +00:00
|
|
|
|
2016-07-18 10:50:31 +00:00
|
|
|
return null;
|
2014-11-12 01:33:47 +00:00
|
|
|
}
|
2015-09-03 22:10:48 +00:00
|
|
|
|
2016-06-08 23:38:52 +00:00
|
|
|
private _mergeWithPropertyMetadata(
|
2016-09-13 02:14:17 +00:00
|
|
|
dm: Directive, propertyMetadata: {[key: string]: any[]},
|
|
|
|
|
directiveType: Type<any>): Directive {
|
2016-09-27 22:41:37 +00:00
|
|
|
const inputs: string[] = [];
|
|
|
|
|
const outputs: string[] = [];
|
|
|
|
|
const host: {[key: string]: string} = {};
|
|
|
|
|
const queries: {[key: string]: any} = {};
|
|
|
|
|
|
|
|
|
|
Object.keys(propertyMetadata).forEach((propName: string) => {
|
2015-09-03 22:10:48 +00:00
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
propertyMetadata[propName].forEach(a => {
|
2016-09-13 02:14:17 +00:00
|
|
|
if (a instanceof Input) {
|
2016-09-27 22:41:37 +00:00
|
|
|
if (a.bindingPropertyName) {
|
2015-10-01 03:59:23 +00:00
|
|
|
inputs.push(`${propName}: ${a.bindingPropertyName}`);
|
2015-09-03 22:10:48 +00:00
|
|
|
} else {
|
2015-10-01 03:59:23 +00:00
|
|
|
inputs.push(propName);
|
2015-09-03 22:10:48 +00:00
|
|
|
}
|
2016-09-13 02:14:17 +00:00
|
|
|
} else if (a instanceof Output) {
|
|
|
|
|
const output: Output = a;
|
2016-09-27 22:41:37 +00:00
|
|
|
if (output.bindingPropertyName) {
|
2016-09-12 16:44:20 +00:00
|
|
|
outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
2015-09-03 22:10:48 +00:00
|
|
|
} else {
|
2015-10-01 03:59:23 +00:00
|
|
|
outputs.push(propName);
|
2015-09-03 22:10:48 +00:00
|
|
|
}
|
2016-09-13 02:14:17 +00:00
|
|
|
} else if (a instanceof HostBinding) {
|
|
|
|
|
const hostBinding: HostBinding = a;
|
2016-09-27 22:41:37 +00:00
|
|
|
if (hostBinding.hostPropertyName) {
|
2016-09-12 16:44:20 +00:00
|
|
|
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
2015-09-04 21:07:16 +00:00
|
|
|
} else {
|
|
|
|
|
host[`[${propName}]`] = propName;
|
|
|
|
|
}
|
2016-09-13 02:14:17 +00:00
|
|
|
} else if (a instanceof HostListener) {
|
|
|
|
|
const hostListener: HostListener = a;
|
2016-09-27 22:41:37 +00:00
|
|
|
const args = hostListener.args || [];
|
|
|
|
|
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
2016-09-13 02:14:17 +00:00
|
|
|
} else if (a instanceof Query) {
|
2015-09-20 01:39:35 +00:00
|
|
|
queries[propName] = a;
|
|
|
|
|
}
|
2015-09-03 22:10:48 +00:00
|
|
|
});
|
|
|
|
|
});
|
2015-11-04 09:08:51 +00:00
|
|
|
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
2015-09-03 22:10:48 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-11 23:01:49 +00:00
|
|
|
private _extractPublicName(def: string) { return splitAtColon(def, [null, def])[1].trim(); }
|
|
|
|
|
|
2016-06-08 23:38:52 +00:00
|
|
|
private _merge(
|
2016-09-27 22:41:37 +00:00
|
|
|
directive: Directive, inputs: string[], outputs: string[], host: {[key: string]: string},
|
2016-09-13 02:14:17 +00:00
|
|
|
queries: {[key: string]: any}, directiveType: Type<any>): Directive {
|
2016-09-27 22:41:37 +00:00
|
|
|
const mergedInputs: string[] = inputs;
|
2016-07-11 23:01:49 +00:00
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
if (directive.inputs) {
|
2016-07-11 23:01:49 +00:00
|
|
|
const inputNames: string[] =
|
2016-09-27 22:41:37 +00:00
|
|
|
directive.inputs.map((def: string): string => this._extractPublicName(def));
|
|
|
|
|
|
2016-07-11 23:01:49 +00:00
|
|
|
inputs.forEach((inputDef: string) => {
|
|
|
|
|
const publicName = this._extractPublicName(inputDef);
|
|
|
|
|
if (inputNames.indexOf(publicName) > -1) {
|
2016-08-25 07:50:16 +00:00
|
|
|
throw new Error(
|
2016-07-11 23:01:49 +00:00
|
|
|
`Input '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-09-27 22:41:37 +00:00
|
|
|
|
|
|
|
|
mergedInputs.unshift(...directive.inputs);
|
2016-07-11 23:01:49 +00:00
|
|
|
}
|
|
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
let mergedOutputs: string[] = outputs;
|
2015-11-04 09:08:51 +00:00
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
if (directive.outputs) {
|
2016-07-11 23:01:49 +00:00
|
|
|
const outputNames: string[] =
|
2016-09-27 22:41:37 +00:00
|
|
|
directive.outputs.map((def: string): string => this._extractPublicName(def));
|
2016-07-11 23:01:49 +00:00
|
|
|
|
|
|
|
|
outputs.forEach((outputDef: string) => {
|
|
|
|
|
const publicName = this._extractPublicName(outputDef);
|
|
|
|
|
if (outputNames.indexOf(publicName) > -1) {
|
2016-08-25 07:50:16 +00:00
|
|
|
throw new Error(
|
2016-07-11 23:01:49 +00:00
|
|
|
`Output event '${publicName}' defined multiple times in '${stringify(directiveType)}'`);
|
2015-11-04 09:08:51 +00:00
|
|
|
}
|
|
|
|
|
});
|
2016-09-27 22:41:37 +00:00
|
|
|
mergedOutputs.unshift(...directive.outputs);
|
2015-11-04 09:08:51 +00:00
|
|
|
}
|
|
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host;
|
|
|
|
|
const mergedQueries =
|
|
|
|
|
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
|
2015-09-03 22:10:48 +00:00
|
|
|
|
2016-09-27 22:41:37 +00:00
|
|
|
if (directive instanceof Component) {
|
2016-09-13 02:14:17 +00:00
|
|
|
return new Component({
|
2016-09-27 22:41:37 +00:00
|
|
|
selector: directive.selector,
|
2015-10-01 03:59:23 +00:00
|
|
|
inputs: mergedInputs,
|
|
|
|
|
outputs: mergedOutputs,
|
2015-09-04 21:07:16 +00:00
|
|
|
host: mergedHost,
|
2016-09-27 22:41:37 +00:00
|
|
|
exportAs: directive.exportAs,
|
|
|
|
|
moduleId: directive.moduleId,
|
2015-09-18 01:45:49 +00:00
|
|
|
queries: mergedQueries,
|
2016-09-27 22:41:37 +00:00
|
|
|
changeDetection: directive.changeDetection,
|
|
|
|
|
providers: directive.providers,
|
|
|
|
|
viewProviders: directive.viewProviders,
|
|
|
|
|
entryComponents: directive.entryComponents,
|
|
|
|
|
template: directive.template,
|
|
|
|
|
templateUrl: directive.templateUrl,
|
|
|
|
|
styles: directive.styles,
|
|
|
|
|
styleUrls: directive.styleUrls,
|
|
|
|
|
encapsulation: directive.encapsulation,
|
|
|
|
|
animations: directive.animations,
|
|
|
|
|
interpolation: directive.interpolation
|
2015-09-03 22:10:48 +00:00
|
|
|
});
|
|
|
|
|
} else {
|
2016-09-13 02:14:17 +00:00
|
|
|
return new Directive({
|
2016-09-27 22:41:37 +00:00
|
|
|
selector: directive.selector,
|
2015-10-01 03:59:23 +00:00
|
|
|
inputs: mergedInputs,
|
|
|
|
|
outputs: mergedOutputs,
|
2015-09-04 21:07:16 +00:00
|
|
|
host: mergedHost,
|
2016-09-27 22:41:37 +00:00
|
|
|
exportAs: directive.exportAs,
|
2015-10-11 05:11:13 +00:00
|
|
|
queries: mergedQueries,
|
2016-09-27 22:41:37 +00:00
|
|
|
providers: directive.providers
|
2015-09-03 22:10:48 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-12 01:33:47 +00:00
|
|
|
}
|
2016-09-27 22:41:37 +00:00
|
|
|
|
|
|
|
|
function isDirectiveMetadata(type: any): type is Directive {
|
|
|
|
|
return type instanceof Directive;
|
|
|
|
|
}
|