feat(Directive): Have a single Directive.host which mimics HTML

fixes #2268

BREAKING CHANGE:

Before

    @Directive({
      hostListeners: {'event': 'statement'},
      hostProperties: {'expression': 'hostProp'},
      hostAttributes: {'attr': 'value'},
      hostActions: {'action': 'statement'}
    })

After

    @Directive({
      host: {
        '(event)': 'statement',
        '[hostProp]': 'expression'  // k & v swapped
        'attr': 'value',
        '@action': 'statement'
      }
    })
This commit is contained in:
Victor Berchet
2015-06-09 12:33:40 +02:00
committed by Tobias Bosch
parent 47b6b05017
commit f3b49378e4
32 changed files with 316 additions and 242 deletions

View File

@ -1,6 +1,6 @@
import {isPresent} from 'angular2/src/facade/lang';
import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {List, Map} from 'angular2/src/facade/collection';
import {List, Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {ASTWithSource} from 'angular2/change_detection';
/**
@ -123,6 +123,11 @@ export class ProtoViewDto {
}
}
// group 1: property from "[property]"
// group 2: event from "(event)"
// group 3: action from "@action"
var hostRegExp = RegExpWrapper.create('^(?:(?:\\[([^\\]]+)\\])|(?:\\(([^\\)]+)\\))|(?:@(.+)))$');
export class DirectiveMetadata {
static get DIRECTIVE_TYPE() { return 0; }
static get COMPONENT_TYPE() { return 1; }
@ -130,10 +135,6 @@ export class DirectiveMetadata {
selector: string;
compileChildren: boolean;
events: List<string>;
hostListeners: Map<string, string>;
hostProperties: Map<string, string>;
hostAttributes: Map<string, string>;
hostActions: Map<string, string>;
properties: List<string>;
readAttributes: List<string>;
type: number;
@ -144,6 +145,11 @@ export class DirectiveMetadata {
callOnAllChangesDone: boolean;
changeDetection: string;
exportAs: string;
hostListeners: Map<string, string>;
hostProperties: Map<string, string>;
hostAttributes: Map<string, string>;
hostActions: Map<string, string>;
constructor({id, selector, compileChildren, events, hostListeners, hostProperties, hostAttributes,
hostActions, properties, readAttributes, type, callOnDestroy, callOnChange,
callOnCheck, callOnInit, callOnAllChangesDone, changeDetection, exportAs}: {
@ -171,8 +177,8 @@ export class DirectiveMetadata {
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
this.events = events;
this.hostListeners = hostListeners;
this.hostProperties = hostProperties;
this.hostAttributes = hostAttributes;
this.hostProperties = hostProperties;
this.hostActions = hostActions;
this.properties = properties;
this.readAttributes = readAttributes;
@ -185,6 +191,76 @@ export class DirectiveMetadata {
this.changeDetection = changeDetection;
this.exportAs = exportAs;
}
static create({id, selector, compileChildren, events, host, properties, readAttributes, type,
callOnDestroy, callOnChange, callOnCheck, callOnInit, callOnAllChangesDone,
changeDetection, exportAs}: {
id?: string,
selector?: string,
compileChildren?: boolean,
events?: List<string>,
host?: Map<string, string>,
properties?: List<string>,
readAttributes?: List<string>,
type?: number,
callOnDestroy?: boolean,
callOnChange?: boolean,
callOnCheck?: boolean,
callOnInit?: boolean,
callOnAllChangesDone?: boolean,
changeDetection?: string,
exportAs?: string
}) {
let hostConfig = DirectiveMetadata.parseHostConfig(host);
return new DirectiveMetadata({
id: id,
selector: selector,
compileChildren: compileChildren,
events: events,
hostListeners: StringMapWrapper.get(hostConfig, 'hostListeners'),
hostProperties: StringMapWrapper.get(hostConfig, 'hostProperties'),
hostAttributes: StringMapWrapper.get(hostConfig, 'hostAttributes'),
hostActions: StringMapWrapper.get(hostConfig, 'hostActions'),
properties: properties,
readAttributes: readAttributes,
type: type,
callOnDestroy: callOnDestroy,
callOnChange: callOnChange,
callOnCheck: callOnCheck,
callOnInit: callOnInit,
callOnAllChangesDone: callOnAllChangesDone,
changeDetection: changeDetection,
exportAs: exportAs
});
}
static parseHostConfig(host?: Map<string, string>): StringMap<string, Map<string, string>> {
let hostListeners = MapWrapper.create();
let hostProperties = MapWrapper.create();
let hostAttributes = MapWrapper.create();
let hostActions = MapWrapper.create();
if (isPresent(host)) {
MapWrapper.forEach(host, (value: string, key: string) => {
var matches = RegExpWrapper.firstMatch(hostRegExp, key);
if (isBlank(matches)) {
MapWrapper.set(hostAttributes, key, value);
} else if (isPresent(matches[1])) {
MapWrapper.set(hostProperties, matches[1], value);
} else if (isPresent(matches[2])) {
MapWrapper.set(hostListeners, matches[2], value);
} else if (isPresent(matches[3])) {
MapWrapper.set(hostActions, matches[3], value);
}
});
}
return {
hostListeners: hostListeners, hostProperties: hostProperties, hostAttributes: hostAttributes,
hostActions: hostActions
}
}
}
// An opaque reference to a DomProtoView

View File

@ -85,37 +85,36 @@ import {
}
});
ListWrapper.forEach(foundDirectiveIndices, (directiveIndex) => {
var directive = this._directives[directiveIndex];
var dirMetadata = this._directives[directiveIndex];
var directiveBinderBuilder = elementBinder.bindDirective(directiveIndex);
current.compileChildren = current.compileChildren && directive.compileChildren;
if (isPresent(directive.properties)) {
ListWrapper.forEach(directive.properties, (bindConfig) => {
current.compileChildren = current.compileChildren && dirMetadata.compileChildren;
if (isPresent(dirMetadata.properties)) {
ListWrapper.forEach(dirMetadata.properties, (bindConfig) => {
this._bindDirectiveProperty(bindConfig, current, directiveBinderBuilder);
});
}
if (isPresent(directive.hostListeners)) {
MapWrapper.forEach(directive.hostListeners, (action, eventName) => {
if (isPresent(dirMetadata.hostListeners)) {
MapWrapper.forEach(dirMetadata.hostListeners, (action, eventName) => {
this._bindDirectiveEvent(eventName, action, current, directiveBinderBuilder);
});
}
if (isPresent(directive.hostActions)) {
MapWrapper.forEach(directive.hostActions, (action, actionName) => {
if (isPresent(dirMetadata.hostActions)) {
MapWrapper.forEach(dirMetadata.hostActions, (action, actionName) => {
this._bindHostAction(actionName, action, current, directiveBinderBuilder);
});
}
if (isPresent(directive.hostProperties)) {
MapWrapper.forEach(directive.hostProperties, (hostPropertyName, directivePropertyName) => {
this._bindHostProperty(hostPropertyName, directivePropertyName, current,
directiveBinderBuilder);
if (isPresent(dirMetadata.hostProperties)) {
MapWrapper.forEach(dirMetadata.hostProperties, (expression, hostPropertyName) => {
this._bindHostProperty(hostPropertyName, expression, current, directiveBinderBuilder);
});
}
if (isPresent(directive.hostAttributes)) {
MapWrapper.forEach(directive.hostAttributes, (hostAttrValue, hostAttrName) => {
if (isPresent(dirMetadata.hostAttributes)) {
MapWrapper.forEach(dirMetadata.hostAttributes, (hostAttrValue, hostAttrName) => {
this._addHostAttribute(hostAttrName, hostAttrValue, current);
});
}
if (isPresent(directive.readAttributes)) {
ListWrapper.forEach(directive.readAttributes,
if (isPresent(dirMetadata.readAttributes)) {
ListWrapper.forEach(dirMetadata.readAttributes,
(attrName) => { elementBinder.readAttribute(attrName); });
}
});
@ -176,9 +175,8 @@ import {
directiveBinderBuilder.bindHostAction(actionName, actionExpression, ast);
}
_bindHostProperty(hostPropertyName, directivePropertyName, compileElement,
directiveBinderBuilder) {
var ast = this._parser.parseBinding(directivePropertyName,
_bindHostProperty(hostPropertyName, expression, compileElement, directiveBinderBuilder) {
var ast = this._parser.parseBinding(expression,
`hostProperties of ${compileElement.elementDescription}`);
directiveBinderBuilder.bindHostProperty(hostPropertyName, ast);
}

View File

@ -11,10 +11,10 @@ export function directiveMetadataToMap(meta: DirectiveMetadata): Map<string, any
['id', meta.id],
['selector', meta.selector],
['compileChildren', meta.compileChildren],
['hostListeners', _cloneIfPresent(meta.hostListeners)],
['hostProperties', _cloneIfPresent(meta.hostProperties)],
['hostAttributes', _cloneIfPresent(meta.hostAttributes)],
['hostListeners', _cloneIfPresent(meta.hostListeners)],
['hostActions', _cloneIfPresent(meta.hostActions)],
['hostAttributes', _cloneIfPresent(meta.hostAttributes)],
['properties', _cloneIfPresent(meta.properties)],
['readAttributes', _cloneIfPresent(meta.readAttributes)],
['type', meta.type],
@ -38,8 +38,8 @@ export function directiveMetadataFromMap(map: Map<string, any>): DirectiveMetada
id:<string>MapWrapper.get(map, 'id'),
selector:<string>MapWrapper.get(map, 'selector'),
compileChildren:<boolean>MapWrapper.get(map, 'compileChildren'),
hostListeners:<Map<string, string>>_cloneIfPresent(MapWrapper.get(map, 'hostListeners')),
hostProperties:<Map<string, string>>_cloneIfPresent(MapWrapper.get(map, 'hostProperties')),
hostListeners:<Map<string, string>>_cloneIfPresent(MapWrapper.get(map, 'hostListeners')),
hostActions:<Map<string, string>>_cloneIfPresent(MapWrapper.get(map, 'hostActions')),
hostAttributes:<Map<string, string>>_cloneIfPresent(MapWrapper.get(map, 'hostAttributes')),
properties:<List<string>>_cloneIfPresent(MapWrapper.get(map, 'properties')),
@ -57,7 +57,7 @@ export function directiveMetadataFromMap(map: Map<string, any>): DirectiveMetada
/**
* Clones the [List] or [Map] `o` if it is present.
*/
function _cloneIfPresent(o) {
function _cloneIfPresent(o): any {
if (!isPresent(o)) return null;
return ListWrapper.isList(o) ? ListWrapper.clone(o) : MapWrapper.clone(o);
}

View File

@ -48,11 +48,11 @@ export class ProtoViewBuilder {
var apiElementBinders = [];
var transitiveContentTagCount = 0;
ListWrapper.forEach(this.elements, (ebb) => {
ListWrapper.forEach(this.elements, (ebb: ElementBinderBuilder) => {
var propertySetters = MapWrapper.create();
var hostActions = MapWrapper.create();
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (dbb) => {
var apiDirectiveBinders = ListWrapper.map(ebb.directives, (dbb: DirectiveBuilder) => {
ebb.eventBuilder.merge(dbb.eventBuilder);
MapWrapper.forEach(dbb.hostPropertyBindings, (_, hostPropertyName) => {