diff --git a/modules/angular2/src/core/annotations_impl/annotations.ts b/modules/angular2/src/core/annotations_impl/annotations.ts
index 3501d3f23a..7e32907cc8 100644
--- a/modules/angular2/src/core/annotations_impl/annotations.ts
+++ b/modules/angular2/src/core/annotations_impl/annotations.ts
@@ -746,9 +746,35 @@ export class Directive extends Injectable {
*/
hostInjector: List;
+ /**
+ * Defines the name that can be used in the template to assign this directive to a variable.
+ *
+ * ## Simple Example
+ *
+ * @Directive({
+ * selector: 'child-dir',
+ * exportAs: 'child'
+ * })
+ * class ChildDir {
+ * }
+ *
+ * @Component({
+ * selector: 'main',
+ * })
+ * @View({
+ * template: ``,
+ * directives: [ChildDir]
+ * })
+ * class MainComponent {
+ * }
+ *
+ * ```
+ */
+ exportAs: string;
+
constructor({
selector, properties, events, hostListeners, hostProperties, hostAttributes,
- hostActions, lifecycle, hostInjector, compileChildren = true,
+ hostActions, lifecycle, hostInjector, exportAs, compileChildren = true,
}: {
selector?: string,
properties?: List,
@@ -759,6 +785,7 @@ export class Directive extends Injectable {
hostActions?: StringMap,
lifecycle?: List,
hostInjector?: List,
+ exportAs?: string,
compileChildren?: boolean
} = {}) {
super();
@@ -769,6 +796,7 @@ export class Directive extends Injectable {
this.hostProperties = hostProperties;
this.hostAttributes = hostAttributes;
this.hostActions = hostActions;
+ this.exportAs = exportAs;
this.lifecycle = lifecycle;
this.compileChildren = compileChildren;
this.hostInjector = hostInjector;
@@ -973,7 +1001,7 @@ export class Component extends Directive {
viewInjector: List;
constructor({selector, properties, events, hostListeners, hostProperties, hostAttributes,
- hostActions, appInjector, lifecycle, hostInjector, viewInjector,
+ hostActions, exportAs, appInjector, lifecycle, hostInjector, viewInjector,
changeDetection = DEFAULT, compileChildren = true}: {
selector?: string,
properties?: List,
@@ -982,6 +1010,7 @@ export class Component extends Directive {
hostProperties?: StringMap,
hostAttributes?: StringMap,
hostActions?: StringMap,
+ exportAs?: string,
appInjector?: List,
lifecycle?: List,
hostInjector?: List,
@@ -997,6 +1026,7 @@ export class Component extends Directive {
hostProperties: hostProperties,
hostAttributes: hostAttributes,
hostActions: hostActions,
+ exportAs: exportAs,
hostInjector: hostInjector,
lifecycle: lifecycle,
compileChildren: compileChildren
diff --git a/modules/angular2/src/core/compiler/element_binder.ts b/modules/angular2/src/core/compiler/element_binder.ts
index 7038d39554..9e629de41a 100644
--- a/modules/angular2/src/core/compiler/element_binder.ts
+++ b/modules/angular2/src/core/compiler/element_binder.ts
@@ -8,8 +8,10 @@ import * as viewModule from './view';
export class ElementBinder {
nestedProtoView: viewModule.AppProtoView;
hostListeners: StringMap>;
+
constructor(public index: int, public parent: ElementBinder, public distanceToParent: int,
public protoElementInjector: eiModule.ProtoElementInjector,
+ public directiveVariableBindings: Map,
public componentDirective: DirectiveBinding) {
if (isBlank(index)) {
throw new BaseException('null index not allowed.');
diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts
index 791a8d8f18..1ddc95dcc8 100644
--- a/modules/angular2/src/core/compiler/element_injector.ts
+++ b/modules/angular2/src/core/compiler/element_injector.ts
@@ -317,7 +317,9 @@ export class DirectiveBinding extends ResolvedBinding {
callOnAllChangesDone: hasLifecycleHook(onAllChangesDone, rb.key.token, ann),
changeDetection: ann instanceof
- Component ? ann.changeDetection : null
+ Component ? ann.changeDetection : null,
+
+ exportAs: ann.exportAs
});
return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise,
resolvedAppInjectables, resolvedHostInjectables,
@@ -422,15 +424,6 @@ export class ProtoElementInjector {
eventEmitterAccessors: List>;
hostActionAccessors: List>;
- /** Whether the element is exported as $implicit. */
- exportElement: boolean;
-
- /** Whether the component instance is exported as $implicit. */
- exportComponent: boolean;
-
- /** The variable name that will be set to $implicit for the element. */
- exportImplicitName: string;
-
_strategy: _ProtoElementInjectorStrategy;
static create(parent: ProtoElementInjector, index: number, bindings: List,
@@ -483,9 +476,6 @@ export class ProtoElementInjector {
constructor(public parent: ProtoElementInjector, public index: int, bd: List,
public distanceToParent: number, public _firstBindingIsComponent: boolean) {
- this.exportComponent = false;
- this.exportElement = false;
-
var length = bd.length;
this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
this.hostActionAccessors = ListWrapper.createFixedSize(length);
@@ -1164,15 +1154,6 @@ export class ElementInjector extends TreeNode {
hasInstances(): boolean { return this._constructionCounter > 0; }
- /** Gets whether this element is exporting a component instance as $implicit. */
- isExportingComponent(): boolean { return this._proto.exportComponent; }
-
- /** Gets whether this element is exporting its element as $implicit. */
- isExportingElement(): boolean { return this._proto.exportElement; }
-
- /** Get the name to which this element's $implicit is to be assigned. */
- getExportImplicitName(): string { return this._proto.exportImplicitName; }
-
getLightDomAppInjector(): Injector { return this._lightDomAppInjector; }
getShadowDomAppInjector(): Injector { return this._shadowDomAppInjector; }
diff --git a/modules/angular2/src/core/compiler/proto_view_factory.ts b/modules/angular2/src/core/compiler/proto_view_factory.ts
index 5a35a7c71f..b55cb69a76 100644
--- a/modules/angular2/src/core/compiler/proto_view_factory.ts
+++ b/modules/angular2/src/core/compiler/proto_view_factory.ts
@@ -1,7 +1,7 @@
import {Injectable} from 'angular2/di';
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
-import {isPresent, isBlank} from 'angular2/src/facade/lang';
+import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {
@@ -309,7 +309,7 @@ function _createElementBinders(protoView, elementBinders, allDirectiveBindings)
componentDirectiveBinding, directiveBindings);
_createElementBinder(protoView, i, renderElementBinder, protoElementInjector,
- componentDirectiveBinding);
+ componentDirectiveBinding, directiveBindings);
}
}
@@ -343,28 +343,20 @@ function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderE
parentPeiWithDistance.protoElementInjector, binderIndex, directiveBindings,
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance);
protoElementInjector.attributes = renderElementBinder.readAttributes;
- if (hasVariables) {
- protoElementInjector.exportComponent = isPresent(componentDirectiveBinding);
- protoElementInjector.exportElement = isBlank(componentDirectiveBinding);
-
- // experiment
- var exportImplicitName = MapWrapper.get(renderElementBinder.variableBindings, '\$implicit');
- if (isPresent(exportImplicitName)) {
- protoElementInjector.exportImplicitName = exportImplicitName;
- }
- }
}
return protoElementInjector;
}
function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
- protoElementInjector, componentDirectiveBinding): ElementBinder {
+ protoElementInjector, componentDirectiveBinding, directiveBindings): ElementBinder {
var parent = null;
if (renderElementBinder.parentIndex !== -1) {
parent = protoView.elementBinders[renderElementBinder.parentIndex];
}
+
+ var directiveVariableBindings = createDirectiveVariableBindings(renderElementBinder, directiveBindings);
var elBinder = protoView.bindElement(parent, renderElementBinder.distanceToParent,
- protoElementInjector, componentDirectiveBinding);
+ protoElementInjector, directiveVariableBindings, componentDirectiveBinding);
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
// variables
// The view's locals needs to have a full set of variable names at construction time
@@ -377,6 +369,49 @@ function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
return elBinder;
}
+export function createDirectiveVariableBindings(renderElementBinder:renderApi.ElementBinder,
+ directiveBindings:List): Map {
+ var directiveVariableBindings = MapWrapper.create();
+ MapWrapper.forEach(renderElementBinder.variableBindings, (templateName, exportAs) => {
+ var dirIndex = _findDirectiveIndexByExportAs(renderElementBinder, directiveBindings, exportAs);
+ MapWrapper.set(directiveVariableBindings, templateName, dirIndex);
+ });
+ return directiveVariableBindings;
+}
+
+function _findDirectiveIndexByExportAs(renderElementBinder, directiveBindings, exportAs) {
+ var matchedDirectiveIndex = null;
+ var matchedDirective;
+
+ for (var i = 0; i < directiveBindings.length; ++i) {
+ var directive = directiveBindings[i];
+
+ if (_directiveExportAs(directive) == exportAs) {
+ if (isPresent(matchedDirective)) {
+ throw new BaseException(`More than one directive have exportAs = '${exportAs}'. Directives: [${matchedDirective.displayName}, ${directive.displayName}]`);
+ }
+
+ matchedDirectiveIndex = i;
+ matchedDirective = directive;
+ }
+ }
+
+ if (isBlank(matchedDirective) && exportAs !== "$implicit") {
+ throw new BaseException(`Cannot find directive with exportAs = '${exportAs}'`);
+ }
+
+ return matchedDirectiveIndex;
+}
+
+function _directiveExportAs(directive):string {
+ var directiveExportAs = directive.metadata.exportAs;
+ if (isBlank(directiveExportAs) && directive.metadata.type === renderApi.DirectiveMetadata.COMPONENT_TYPE) {
+ return "$implicit";
+ } else {
+ return directiveExportAs;
+ }
+}
+
function _bindDirectiveEvents(protoView, elementBinders: List) {
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; ++boundElementIndex) {
var dirs = elementBinders[boundElementIndex].directives;
diff --git a/modules/angular2/src/core/compiler/view.ts b/modules/angular2/src/core/compiler/view.ts
index fa55fc336b..2b383f40ed 100644
--- a/modules/angular2/src/core/compiler/view.ts
+++ b/modules/angular2/src/core/compiler/view.ts
@@ -183,9 +183,12 @@ export class AppProtoView {
bindElement(parent: ElementBinder, distanceToParent: int,
protoElementInjector: ProtoElementInjector,
+ directiveVariableBindings: Map,
componentDirective: DirectiveBinding = null): ElementBinder {
- var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
- protoElementInjector, componentDirective);
+ var elBinder =
+ new ElementBinder(this.elementBinders.length, parent, distanceToParent,
+ protoElementInjector, directiveVariableBindings, componentDirective);
+
ListWrapper.push(this.elementBinders, elBinder);
return elBinder;
}
diff --git a/modules/angular2/src/core/compiler/view_manager_utils.ts b/modules/angular2/src/core/compiler/view_manager_utils.ts
index a070263073..46cf5720f0 100644
--- a/modules/angular2/src/core/compiler/view_manager_utils.ts
+++ b/modules/angular2/src/core/compiler/view_manager_utils.ts
@@ -206,20 +206,22 @@ export class AppViewManagerUtils {
var binders = view.proto.elementBinders;
for (var i = 0; i < binders.length; ++i) {
+ var binder = binders[i];
var elementInjector = view.elementInjectors[i];
+
if (isPresent(elementInjector)) {
elementInjector.hydrate(appInjector, hostElementInjector, view.preBuiltObjects[i]);
this._setUpEventEmitters(view, elementInjector, i);
this._setUpHostActions(view, elementInjector, i);
- // The exporting of $implicit is a special case. Since multiple elements will all export
- // the different values as $implicit, directly assign $implicit bindings to the variable
- // name.
- var exportImplicitName = elementInjector.getExportImplicitName();
- if (elementInjector.isExportingComponent()) {
- view.locals.set(exportImplicitName, elementInjector.getComponent());
- } else if (elementInjector.isExportingElement()) {
- view.locals.set(exportImplicitName, elementInjector.getElementRef().domElement);
+ if (isPresent(binder.directiveVariableBindings)) {
+ MapWrapper.forEach(binder.directiveVariableBindings, (directiveIndex, name) => {
+ if (isBlank(directiveIndex)) {
+ view.locals.set(name, elementInjector.getElementRef().domElement);
+ } else {
+ view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
+ }
+ });
}
}
}
diff --git a/modules/angular2/src/render/api.ts b/modules/angular2/src/render/api.ts
index 3abf9ac063..4b8e0f6b1d 100644
--- a/modules/angular2/src/render/api.ts
+++ b/modules/angular2/src/render/api.ts
@@ -36,7 +36,7 @@ export class ElementBinder {
directives: List;
nestedProtoView: ProtoViewDto;
propertyBindings: Map;
- variableBindings: Map;
+ variableBindings: Map;
// Note: this contains a preprocessed AST
// that replaced the values that should be extracted from the element
// with a local name
@@ -52,7 +52,7 @@ export class ElementBinder {
directives?: List,
nestedProtoView?: ProtoViewDto,
propertyBindings?: Map,
- variableBindings?: Map,
+ variableBindings?: Map,
eventBindings?: List,
textBindings?: List,
readAttributes?: Map
@@ -142,9 +142,10 @@ export class DirectiveMetadata {
callOnInit: boolean;
callOnAllChangesDone: boolean;
changeDetection: string;
+ exportAs: string;
constructor({id, selector, compileChildren, events, hostListeners, hostProperties, hostAttributes,
hostActions, properties, readAttributes, type, callOnDestroy, callOnChange,
- callOnCheck, callOnInit, callOnAllChangesDone, changeDetection}: {
+ callOnCheck, callOnInit, callOnAllChangesDone, changeDetection, exportAs}: {
id?: string,
selector?: string,
compileChildren?: boolean,
@@ -161,7 +162,8 @@ export class DirectiveMetadata {
callOnCheck?: boolean,
callOnInit?: boolean,
callOnAllChangesDone?: boolean,
- changeDetection?: string
+ changeDetection?: string,
+ exportAs?: string
}) {
this.id = id;
this.selector = selector;
@@ -180,6 +182,7 @@ export class DirectiveMetadata {
this.callOnInit = callOnInit;
this.callOnAllChangesDone = callOnAllChangesDone;
this.changeDetection = changeDetection;
+ this.exportAs = exportAs;
}
}
diff --git a/modules/angular2/src/render/dom/convert.ts b/modules/angular2/src/render/dom/convert.ts
index 960274466a..87471dae23 100644
--- a/modules/angular2/src/render/dom/convert.ts
+++ b/modules/angular2/src/render/dom/convert.ts
@@ -18,6 +18,7 @@ export function directiveMetadataToMap(meta: DirectiveMetadata): Map): DirectiveMetada
properties:>_cloneIfPresent(MapWrapper.get(map, 'properties')),
readAttributes:>_cloneIfPresent(MapWrapper.get(map, 'readAttributes')),
type:MapWrapper.get(map, 'type'),
+ exportAs:MapWrapper.get(map, 'exportAs'),
callOnDestroy:MapWrapper.get(map, 'callOnDestroy'),
callOnCheck:MapWrapper.get(map, 'callOnCheck'),
callOnChange:MapWrapper.get(map, 'callOnChange'),
diff --git a/modules/angular2/test/core/compiler/compiler_spec.ts b/modules/angular2/test/core/compiler/compiler_spec.ts
index f68dacccc7..87e0c6e93d 100644
--- a/modules/angular2/test/core/compiler/compiler_spec.ts
+++ b/modules/angular2/test/core/compiler/compiler_spec.ts
@@ -420,11 +420,11 @@ function createProtoView(elementBinders = null) {
function createComponentElementBinder(directiveResolver, type) {
var binding = createDirectiveBinding(directiveResolver, type);
- return new ElementBinder(0, null, 0, null, binding);
+ return new ElementBinder(0, null, 0, null, null, binding);
}
function createViewportElementBinder(nestedProtoView) {
- var elBinder = new ElementBinder(0, null, 0, null, null);
+ var elBinder = new ElementBinder(0, null, 0, null, null, null);
elBinder.nestedProtoView = nestedProtoView;
return elBinder;
}
diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts
index b1815230a5..4e6a78100d 100644
--- a/modules/angular2/test/core/compiler/integration_spec.ts
+++ b/modules/angular2/test/core/compiler/integration_spec.ts
@@ -422,110 +422,127 @@ export function main() {
});
}));
- it('should assign the component instance to a var-',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(MyComp, new viewAnn.View({
- template: '
',
- directives: [ChildComp]
+ describe("variable bindings", () => {
+ it('should assign a component to a var-',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new viewAnn.View({
+ template: '
',
+ directives: [ChildComp]
+ }));
+
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
+ expect(view.rawView.locals).not.toBe(null);
+ expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
+
+ async.done();
+ })
}));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
- expect(view.rawView.locals).not.toBe(null);
- expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
+ it('should assign a directive to a var-',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new viewAnn.View({
+ template: '
',
+ directives: [ExportDir]
+ }));
- async.done();
- })
- }));
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
+ expect(view.rawView.locals).not.toBe(null);
+ expect(view.rawView.locals.get('localdir')).toBeAnInstanceOf(ExportDir);
- it('should make the assigned component accessible in property bindings',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(MyComp, new viewAnn.View({
- template: '{{alice.ctxProp}}
',
- directives: [ChildComp]
+ async.done();
+ });
}));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
- view.detectChanges();
+ it('should make the assigned component accessible in property bindings',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new viewAnn.View({
+ template: '{{alice.ctxProp}}
',
+ directives: [ChildComp]
+ }));
- expect(view.rootNodes).toHaveText('hellohello'); // this first one is the
- // component, the second one is
- // the text binding
- async.done();
- })
- }));
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
+ view.detectChanges();
- it('should assign two component instances each with a var-',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(MyComp, new viewAnn.View({
- template: '
',
- directives: [ChildComp]
+ expect(view.rootNodes).toHaveText('hellohello'); // this first one is the
+ // component, the second one is
+ // the text binding
+ async.done();
+ })
}));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
+ it('should assign two component instances each with a var-',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new viewAnn.View({
+ template: '
',
+ directives: [ChildComp]
+ }));
- expect(view.rawView.locals).not.toBe(null);
- expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
- expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp);
- expect(view.rawView.locals.get('alice')).not.toBe(view.rawView.locals.get('bob'));
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
- async.done();
- })
- }));
+ expect(view.rawView.locals).not.toBe(null);
+ expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
+ expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp);
+ expect(view.rawView.locals.get('alice'))
+ .not.toBe(view.rawView.locals.get('bob'));
- it('should assign the component instance to a var- with shorthand syntax',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(
- MyComp, new viewAnn.View(
- {template: '', directives: [ChildComp]}));
+ async.done();
+ })
+ }));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
+ it('should assign the component instance to a var- with shorthand syntax',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(
+ MyComp,
+ new viewAnn.View(
+ {template: '', directives: [ChildComp]}));
- expect(view.rawView.locals).not.toBe(null);
- expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
- async.done();
- })
- }));
+ expect(view.rawView.locals).not.toBe(null);
+ expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
- it('should assign the element instance to a user-defined variable',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(
- MyComp, new viewAnn.View({template: 'Hello
'}));
+ async.done();
+ })
+ }));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
- expect(view.rawView.locals).not.toBe(null);
+ it('should assign the element instance to a user-defined variable',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(
+ MyComp, new viewAnn.View({template: 'Hello
'}));
- var value = view.rawView.locals.get('alice');
- expect(value).not.toBe(null);
- expect(value.tagName.toLowerCase()).toEqual('div');
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
+ expect(view.rawView.locals).not.toBe(null);
- async.done();
- })
- }));
+ var value = view.rawView.locals.get('alice');
+ expect(value).not.toBe(null);
+ expect(value.tagName.toLowerCase()).toEqual('div');
+ async.done();
+ })
+ }));
- it('should assign the element instance to a user-defined variable with camelCase using dash-case',
- inject([TestBed, AsyncTestCompleter], (tb, async) => {
- tb.overrideView(
- MyComp,
- new viewAnn.View({template: 'Hello
'}));
+ it('should change dash-case to camel-case',
+ inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new viewAnn.View({
+ template: '
',
+ directives: [ChildComp]
+ }));
- tb.createView(MyComp, {context: ctx})
- .then((view) => {
- expect(view.rawView.locals).not.toBe(null);
+ tb.createView(MyComp, {context: ctx})
+ .then((view) => {
+ expect(view.rawView.locals).not.toBe(null);
+ expect(view.rawView.locals.get('superAlice')).toBeAnInstanceOf(ChildComp);
- var value = view.rawView.locals.get('superAlice');
- expect(value).not.toBe(null);
- expect(value.tagName.toLowerCase()).toEqual('div');
-
- async.done();
- })
- }));
+ async.done();
+ });
+ }));
+ });
describe("ON_PUSH components", () => {
it("should use ChangeDetectorRef to manually request a check",
@@ -1696,3 +1713,7 @@ class SomeImperativeViewport {
}
}
}
+
+@Directive({selector: '[export-dir]', exportAs: 'dir'})
+class ExportDir {
+}
\ No newline at end of file
diff --git a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts
index 71444f4dfc..31c55cdc70 100644
--- a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts
+++ b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts
@@ -20,9 +20,11 @@ import {MapWrapper} from 'angular2/src/facade/collection';
import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection';
import {
ProtoViewFactory,
- getChangeDetectorDefinitions
+ getChangeDetectorDefinitions,
+ createDirectiveVariableBindings
} from 'angular2/src/core/compiler/proto_view_factory';
import {Component, Directive} from 'angular2/annotations';
+import {Key} from 'angular2/di';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import * as renderApi from 'angular2/src/render/api';
@@ -65,12 +67,85 @@ export function main() {
expect(pvs.length).toBe(1);
expect(pvs[0].render).toBe(renderPv.render);
});
-
});
+ describe("createDirectiveVariableBindings", () => {
+ it("should calculate directive variable bindings", () => {
+ var dvbs = createDirectiveVariableBindings(
+ new renderApi.ElementBinder(
+ {variableBindings: MapWrapper.createFromStringMap({"exportName": "templateName"})}),
+ [
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'otherName'})})
+ ]);
+
+ expect(dvbs).toEqual(MapWrapper.createFromStringMap({"templateName": 0}));
+ });
+
+ it("should set exportAs to $implicit for component with exportAs = null", () => {
+ var dvbs = createDirectiveVariableBindings(
+ new renderApi.ElementBinder(
+ {variableBindings: MapWrapper.createFromStringMap({"$implicit": "templateName"})}),
+ [
+ directiveBinding({
+ metadata: new renderApi.DirectiveMetadata(
+ {exportAs: null, type: renderApi.DirectiveMetadata.COMPONENT_TYPE})
+ })
+ ]);
+
+ expect(dvbs).toEqual(MapWrapper.createFromStringMap({"templateName": 0}));
+ });
+
+ it("should throw we no directive exported with this name", () => {
+ expect(() => {
+ createDirectiveVariableBindings(
+ new renderApi.ElementBinder({
+ variableBindings:
+ MapWrapper.createFromStringMap({"someInvalidName": "templateName"})
+ }),
+ [
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
+ ]);
+ }).toThrowError(new RegExp("Cannot find directive with exportAs = 'someInvalidName'"));
+ });
+
+ it("should throw when binding to a name exported by two directives", () => {
+ expect(() => {
+ createDirectiveVariableBindings(
+ new renderApi.ElementBinder({
+ variableBindings: MapWrapper.createFromStringMap({"exportName": "templateName"})
+ }),
+ [
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
+ ]);
+ }).toThrowError(new RegExp("More than one directive have exportAs = 'exportName'"));
+ });
+
+ it("should not throw when not binding to a name exported by two directives", () => {
+ expect(() => {
+ createDirectiveVariableBindings(
+ new renderApi.ElementBinder({variableBindings: MapWrapper.create()}), [
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})}),
+ directiveBinding(
+ {metadata: new renderApi.DirectiveMetadata({exportAs: 'exportName'})})
+ ]);
+ }).not.toThrow();
+ });
+ });
});
}
+function directiveBinding({metadata}: {metadata?: any} = {}) {
+ return new DirectiveBinding(Key.get("dummy"), null, [], false, [], [], [], metadata);
+}
+
function createRenderProtoView(elementBinders = null, type: number = null) {
if (isBlank(type)) {
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
diff --git a/modules/angular2/test/core/compiler/view_manager_spec.ts b/modules/angular2/test/core/compiler/view_manager_spec.ts
index b33a859eb7..d72b5f3a2a 100644
--- a/modules/angular2/test/core/compiler/view_manager_spec.ts
+++ b/modules/angular2/test/core/compiler/view_manager_spec.ts
@@ -58,11 +58,11 @@ export function main() {
return DirectiveBinding.createFromType(type, annotation);
}
- function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
+ function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null, null); }
function createComponentElBinder(nestedProtoView = null) {
var binding = createDirectiveBinding(SomeComponent);
- var binder = new ElementBinder(0, null, 0, null, binding);
+ var binder = new ElementBinder(0, null, 0, null, null, binding);
binder.nestedProtoView = nestedProtoView;
return binder;
}
diff --git a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts
index 318083e498..71de3793f8 100644
--- a/modules/angular2/test/core/compiler/view_manager_utils_spec.ts
+++ b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts
@@ -48,11 +48,11 @@ export function main() {
return DirectiveBinding.createFromType(type, annotation);
}
- function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
+ function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null, null); }
function createComponentElBinder(nestedProtoView = null) {
var binding = createDirectiveBinding(SomeComponent);
- var binder = new ElementBinder(0, null, 0, null, binding);
+ var binder = new ElementBinder(0, null, 0, null, null, binding);
binder.nestedProtoView = nestedProtoView;
return binder;
}
diff --git a/modules/angular2/test/render/dom/convert_spec.ts b/modules/angular2/test/render/dom/convert_spec.ts
index 71a479c19d..8b924c35a1 100644
--- a/modules/angular2/test/render/dom/convert_spec.ts
+++ b/modules/angular2/test/render/dom/convert_spec.ts
@@ -16,6 +16,7 @@ export function main() {
readAttributes: ['read1', 'read2'],
selector: 'some-comp',
type: DirectiveMetadata.COMPONENT_TYPE,
+ exportAs: 'aaa',
callOnDestroy: true,
callOnChange: true,
callOnCheck: true,
@@ -40,6 +41,7 @@ export function main() {
expect(MapWrapper.get(map, 'callOnChange')).toEqual(true);
expect(MapWrapper.get(map, 'callOnInit')).toEqual(true);
expect(MapWrapper.get(map, 'callOnAllChangesDone')).toEqual(true);
+ expect(MapWrapper.get(map, 'exportAs')).toEqual('aaa');
});
it('mapToDirectiveMetadata', () => {
@@ -53,6 +55,7 @@ export function main() {
['readAttributes', ['readTest1', 'readTest2']],
['selector', 'testSelector'],
['type', DirectiveMetadata.DIRECTIVE_TYPE],
+ ['exportAs', 'aaa'],
['callOnDestroy', true],
['callOnCheck', true],
['callOnInit', true],
@@ -71,6 +74,7 @@ export function main() {
expect(meta.readAttributes).toEqual(['readTest1', 'readTest2']);
expect(meta.selector).toEqual('testSelector');
expect(meta.type).toEqual(DirectiveMetadata.DIRECTIVE_TYPE);
+ expect(meta.exportAs).toEqual('aaa');
expect(meta.callOnDestroy).toEqual(true);
expect(meta.callOnCheck).toEqual(true);
expect(meta.callOnInit).toEqual(true);