diff --git a/modules/angular2/di.ts b/modules/angular2/di.ts
index 045cce6627..aedb78c01e 100644
--- a/modules/angular2/di.ts
+++ b/modules/angular2/di.ts
@@ -8,7 +8,7 @@
export * from './src/di/annotations';
export * from './src/di/decorators';
export * from './src/di/forward_ref';
-export {Injector} from './src/di/injector';
+export {resolveBindings, Injector} from './src/di/injector';
export {Binding, ResolvedBinding, Dependency, bind} from './src/di/binding';
export {Key, KeyRegistry, TypeLiteral} from './src/di/key';
export {
diff --git a/modules/angular2/src/core/annotations_impl/annotations.js b/modules/angular2/src/core/annotations_impl/annotations.js
index 6f6636c251..e1b4c48da0 100644
--- a/modules/angular2/src/core/annotations_impl/annotations.js
+++ b/modules/angular2/src/core/annotations_impl/annotations.js
@@ -660,6 +660,37 @@ export class Directive extends Injectable {
//TODO(vsavkin): This would better fall under the Macro directive concept.
compileChildren: boolean;
+ /**
+ * Defines the set of injectable objects that are visible to a Directive and its light dom children.
+ *
+ * ## Simple Example
+ *
+ * Here is an example of a class that can be injected:
+ *
+ * ```
+ * class Greeter {
+ * greet(name:string) {
+ * return 'Hello ' + name + '!';
+ * }
+ * }
+ *
+ * @Directive({
+ * selector: 'greet',
+ * hostInjector: [
+ * Greeter
+ * ]
+ * })
+ * class HelloWorld {
+ * greeter:Greeter;
+ *
+ * constructor(greeter:Greeter) {
+ * this.greeter = greeter;
+ * }
+ * }
+ * ```
+ */
+ hostInjector:List;
+
@CONST()
constructor({
selector,
@@ -670,6 +701,7 @@ export class Directive extends Injectable {
hostAttributes,
hostActions,
lifecycle,
+ hostInjector,
compileChildren = true,
}:{
selector:string,
@@ -680,6 +712,7 @@ export class Directive extends Injectable {
hostAttributes: any,
hostActions: any,
lifecycle:List,
+ hostInjector:List,
compileChildren:boolean
}={})
{
@@ -693,6 +726,7 @@ export class Directive extends Injectable {
this.hostActions = hostActions;
this.lifecycle = lifecycle;
this.compileChildren = compileChildren;
+ this.hostInjector = hostInjector;
}
/**
@@ -845,6 +879,48 @@ export class Component extends Directive {
*/
appInjector:List;
+ /**
+ * Defines the set of injectable objects that are visible to its view dom children.
+ *
+ * ## Simple Example
+ *
+ * Here is an example of a class that can be injected:
+ *
+ * ```
+ * class Greeter {
+ * greet(name:string) {
+ * return 'Hello ' + name + '!';
+ * }
+ * }
+ *
+ * @Directive({
+ * selector: 'needs-greeter'
+ * })
+ * class NeedsGreeter {
+ * greeter:Greeter;
+ *
+ * constructor(greeter:Greeter) {
+ * this.greeter = greeter;
+ * }
+ * }
+ *
+ * @Component({
+ * selector: 'greet',
+ * viewInjector: [
+ * Greeter
+ * ]
+ * })
+ * @View({
+ * template: ``,
+ * directives: [NeedsGreeter]
+ * })
+ * class HelloWorld {
+ * }
+ *
+ * ```
+ */
+ viewInjector:List;
+
@CONST()
constructor({
selector,
@@ -856,6 +932,8 @@ export class Component extends Directive {
hostActions,
appInjector,
lifecycle,
+ hostInjector,
+ viewInjector,
changeDetection = DEFAULT,
compileChildren = true
}:{
@@ -868,6 +946,8 @@ export class Component extends Directive {
hostActions:any,
appInjector:List,
lifecycle:List,
+ hostInjector:List,
+ viewInjector:List,
changeDetection:string,
compileChildren:boolean
}={})
@@ -880,12 +960,14 @@ export class Component extends Directive {
hostProperties: hostProperties,
hostAttributes: hostAttributes,
hostActions: hostActions,
+ hostInjector: hostInjector,
lifecycle: lifecycle,
compileChildren: compileChildren
});
this.changeDetection = changeDetection;
this.appInjector = appInjector;
+ this.viewInjector = viewInjector;
}
}
diff --git a/modules/angular2/src/core/annotations_impl/visibility.js b/modules/angular2/src/core/annotations_impl/visibility.js
index 10f47a5233..c878d5165c 100644
--- a/modules/angular2/src/core/annotations_impl/visibility.js
+++ b/modules/angular2/src/core/annotations_impl/visibility.js
@@ -1,6 +1,66 @@
import {CONST} from 'angular2/src/facade/lang';
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
+export class Visibility extends DependencyAnnotation {
+ depth: number;
+ crossComponentBoundaries: boolean;
+
+ @CONST()
+ constructor(depth:number, crossComponentBoundaries:boolean) {
+ super();
+ this.depth = depth;
+ this.crossComponentBoundaries = crossComponentBoundaries;
+ }
+
+ shouldIncludeSelf():boolean {
+ return this.depth === 0;
+ }
+}
+
+/**
+ * Specifies that an injector should retrieve a dependency from its element.
+ *
+ * ## Example
+ *
+ * Here is a simple directive that retrieves a dependency from its element.
+ *
+ * ```
+ * @Directive({
+ * selector: '[dependency]',
+ * properties: {
+ * 'id':'dependency'
+ * }
+ * })
+ * class Dependency {
+ * id:string;
+ * }
+ *
+ *
+ * @Directive({
+ * selector: '[my-directive]'
+ * })
+ * class Dependency {
+ * constructor(@Self() dependency:Dependency) {
+ * expect(dependency.id).toEqual(1);
+ * };
+ * }
+ * ```
+ *
+ * We use this with the following HTML template:
+ *
+ * ```
+ *
+ * ```
+ *
+ * @exportedAs angular2/annotations
+ */
+export class Self extends Visibility {
+ @CONST()
+ constructor() {
+ super(0, false);
+ }
+}
+
/**
* Specifies that an injector should retrieve a dependency from the direct parent.
*
@@ -42,15 +102,15 @@ import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
*
* @exportedAs angular2/annotations
*/
-export class Parent extends DependencyAnnotation {
+export class Parent extends Visibility {
@CONST()
constructor() {
- super();
+ super(1, false);
}
}
/**
- * Specifies that an injector should retrieve a dependency from any ancestor element.
+ * Specifies that an injector should retrieve a dependency from any ancestor element within the same shadow boundary.
*
* An ancestor is any element between the parent element and shadow root.
*
@@ -103,9 +163,50 @@ export class Parent extends DependencyAnnotation {
*
* @exportedAs angular2/annotations
*/
-export class Ancestor extends DependencyAnnotation {
+export class Ancestor extends Visibility {
@CONST()
constructor() {
- super();
+ super(999999, false);
+ }
+}
+
+/**
+ * Specifies that an injector should retrieve a dependency from any ancestor element.
+ *
+ * An ancestor is any element between the parent element and shadow root.
+ *
+ *
+ * ## Example
+ *
+ * Here is a simple directive that retrieves a dependency from an ancestor element.
+ *
+ * ```
+ * @Directive({
+ * selector: '[dependency]',
+ * properties: {
+ * 'id':'dependency'
+ * }
+ * })
+ * class Dependency {
+ * id:string;
+ * }
+ *
+ *
+ * @Directive({
+ * selector: '[my-directive]'
+ * })
+ * class Dependency {
+ * constructor(@Unbounded() dependency:Dependency) {
+ * expect(dependency.id).toEqual(2);
+ * };
+ * }
+ * ```
+ *
+ * @exportedAs angular2/annotations
+ */
+export class Unbounded extends Visibility {
+ @CONST()
+ constructor() {
+ super(999999, true);
}
}
diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js
index 0ceb06101c..2658f0b1ff 100644
--- a/modules/angular2/src/core/compiler/element_injector.js
+++ b/modules/angular2/src/core/compiler/element_injector.js
@@ -1,10 +1,9 @@
import {isPresent, isBlank, Type, int, BaseException, stringify} from 'angular2/src/facade/lang';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
-import {Math} from 'angular2/src/facade/math';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoBindingError,
- AbstractBindingError, CyclicDependencyError, resolveForwardRef} from 'angular2/di';
-import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
+ AbstractBindingError, CyclicDependencyError, resolveForwardRef, resolveBindings} from 'angular2/di';
+import {Visibility, Self} from 'angular2/src/core/annotations_impl/visibility';
import {Attribute, Query} from 'angular2/src/core/annotations_impl/di';
import * as viewModule from './view';
import * as avmModule from './view_manager';
@@ -20,8 +19,6 @@ import {DirectiveMetadata} from 'angular2/src/render/api';
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
-var MAX_DEPTH = Math.pow(2, 30) - 1;
-
var _undefined = new Object();
var _staticKeys;
@@ -177,14 +174,14 @@ export class TreeNode {
}
export class DirectiveDependency extends Dependency {
- depth:int;
+ visibility:Visibility;
attributeName:string;
queryDirective;
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List,
- depth:int, attributeName:string, queryDirective) {
+ visibility:Visibility, attributeName:string, queryDirective) {
super(key, asPromise, lazy, optional, properties);
- this.depth = depth;
+ this.visibility = visibility;;
this.attributeName = attributeName;
this.queryDirective = queryDirective;
this._verify();
@@ -199,18 +196,17 @@ export class DirectiveDependency extends Dependency {
}
static createFrom(d:Dependency):Dependency {
- return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
- d.properties, DirectiveDependency._depth(d.properties),
+ return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties,
+ DirectiveDependency._visibility(d.properties),
DirectiveDependency._attributeName(d.properties),
DirectiveDependency._query(d.properties)
);
}
- static _depth(properties):int {
- if (properties.length == 0) return 0;
- if (ListWrapper.any(properties, p => p instanceof Parent)) return 1;
- if (ListWrapper.any(properties, p => p instanceof Ancestor)) return MAX_DEPTH;
- return 0;
+ static _visibility(properties):Visibility {
+ if (properties.length == 0) return new Self();
+ var p = ListWrapper.find(properties, p => p instanceof Visibility);
+ return isPresent(p) ? p : new Self();
}
static _attributeName(properties):string {
@@ -225,13 +221,18 @@ export class DirectiveDependency extends Dependency {
}
export class DirectiveBinding extends ResolvedBinding {
- resolvedInjectables:List;
+ resolvedAppInjectables:List;
+ resolvedHostInjectables:List;
+ resolvedViewInjectables:List;
metadata: DirectiveMetadata;
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean,
- resolvedInjectables:List, metadata:DirectiveMetadata, annotation: Directive) {
+ resolvedAppInjectables:List, resolvedHostInjectables:List,
+ resolvedViewInjectables:List, metadata:DirectiveMetadata) {
super(key, factory, dependencies, providedAsPromise);
- this.resolvedInjectables = resolvedInjectables;
+ this.resolvedAppInjectables = resolvedAppInjectables;
+ this.resolvedHostInjectables = resolvedHostInjectables;
+ this.resolvedViewInjectables = resolvedViewInjectables;
this.metadata = metadata;
}
@@ -260,59 +261,52 @@ export class DirectiveBinding extends ResolvedBinding {
}
get changeDetection() {
- if (isPresent(metadata)) {
- return metadata.changeDetection;
- } else {
- return null;
- }
+ return this.metadata.changeDetection;
}
- static createFromBinding(b:Binding, ann:Directive):DirectiveBinding {
+ static createFromBinding(binding:Binding, ann:Directive):DirectiveBinding {
if (isBlank(ann)) {
ann = new Directive();
}
- var rb = b.resolve();
+
+ var rb = binding.resolve();
var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
- var renderType;
- var compileChildren = ann.compileChildren;
- var resolvedInjectables = null;
- var changeDetection = null;
- if (ann instanceof Component) {
- renderType = DirectiveMetadata.COMPONENT_TYPE;
- if (isPresent(ann.appInjector)) {
- resolvedInjectables = Injector.resolve(ann.appInjector);
- }
- changeDetection = ann.changeDetection;
- } else {
- renderType = DirectiveMetadata.DIRECTIVE_TYPE;
- }
- var readAttributes = [];
- ListWrapper.forEach(deps, (dep) => {
- if (isPresent(dep.attributeName)) {
- ListWrapper.push(readAttributes, dep.attributeName);
- }
- });
+ var resolvedAppInjectables = ann instanceof Component && isPresent(ann.appInjector) ? Injector.resolve(ann.appInjector) : [];
+ var resolvedHostInjectables = isPresent(ann.hostInjector) ? resolveBindings(ann.hostInjector) : [];
+ var resolvedViewInjectables = ann instanceof Component && isPresent(ann.viewInjector) ? resolveBindings(ann.viewInjector) : [];
+
var metadata = new DirectiveMetadata({
id: stringify(rb.key.token),
- type: renderType,
+ type: ann instanceof Component ? DirectiveMetadata.COMPONENT_TYPE : DirectiveMetadata.DIRECTIVE_TYPE,
selector: ann.selector,
- compileChildren: compileChildren,
+ compileChildren: ann.compileChildren,
events: ann.events,
hostListeners: isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
hostProperties: isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
hostAttributes: isPresent(ann.hostAttributes) ? MapWrapper.createFromStringMap(ann.hostAttributes) : null,
hostActions: isPresent(ann.hostActions) ? MapWrapper.createFromStringMap(ann.hostActions) : null,
properties: isPresent(ann.properties) ? MapWrapper.createFromStringMap(ann.properties) : null,
- readAttributes: readAttributes,
+ readAttributes: DirectiveBinding._readAttributes(deps),
callOnDestroy: ann.hasLifecycleHook(onDestroy),
callOnChange: ann.hasLifecycleHook(onChange),
callOnAllChangesDone: ann.hasLifecycleHook(onAllChangesDone),
- changeDetection: changeDetection
+ changeDetection: ann instanceof Component ? ann.changeDetection : null
});
- return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, resolvedInjectables, metadata, ann);
+ return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise, resolvedAppInjectables,
+ resolvedHostInjectables, resolvedViewInjectables, metadata);
}
- static createFromType(type:Type, annotation:Directive):DirectiveBinding {
+ static _readAttributes(deps) {
+ var readAttributes = [];
+ ListWrapper.forEach(deps, (dep) => {
+ if (isPresent(dep.attributeName)) {
+ ListWrapper.push(readAttributes, dep.attributeName);
+ }
+ });
+ return readAttributes;
+ }
+
+ static createFromType(type:Type, annotation:Directive):DirectiveBinding {
var binding = new Binding(type, {toClass: type});
return DirectiveBinding.createFromBinding(binding, annotation);
}
@@ -363,6 +357,42 @@ class HostActionAccessor {
}
}
+const LIGHT_DOM = 1;
+const SHADOW_DOM = 2;
+const LIGHT_DOM_AND_SHADOW_DOM = 3;
+
+class BindingData {
+ binding:ResolvedBinding;
+ visibility:number;
+
+ constructor(binding:ResolvedBinding, visibility:number) {
+ this.binding = binding;
+ this.visibility = visibility;
+ }
+
+ getKeyId() {
+ return this.binding.key.id;
+ }
+
+ createEventEmitterAccessors() {
+ if (!(this.binding instanceof DirectiveBinding)) return [];
+ var db:DirectiveBinding = this.binding;
+ return ListWrapper.map(db.eventEmitters, eventName =>
+ new EventEmitterAccessor(eventName, reflector.getter(eventName))
+ );
+ }
+
+ createHostActionAccessors() {
+ if (!(this.binding instanceof DirectiveBinding)) return [];
+ var res = [];
+ var db:DirectiveBinding = this.binding;
+ MapWrapper.forEach(db.hostActions, (actionExpression, actionName) => {
+ ListWrapper.push(res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))
+ });
+ return res;
+ }
+}
+
/**
Difference between di.Injector and ElementInjector
@@ -382,20 +412,19 @@ ElementInjector:
PERF BENCHMARK: http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
*/
-
-
export class ProtoElementInjector {
- _binding0:DirectiveBinding;
- _binding1:DirectiveBinding;
- _binding2:DirectiveBinding;
- _binding3:DirectiveBinding;
- _binding4:DirectiveBinding;
- _binding5:DirectiveBinding;
- _binding6:DirectiveBinding;
- _binding7:DirectiveBinding;
- _binding8:DirectiveBinding;
- _binding9:DirectiveBinding;
- _binding0IsComponent:boolean;
+ // only _binding0 can contain a component
+ _binding0:ResolvedBinding;
+ _binding1:ResolvedBinding;
+ _binding2:ResolvedBinding;
+ _binding3:ResolvedBinding;
+ _binding4:ResolvedBinding;
+ _binding5:ResolvedBinding;
+ _binding6:ResolvedBinding;
+ _binding7:ResolvedBinding;
+ _binding8:ResolvedBinding;
+ _binding9:ResolvedBinding;
+
_keyId0:int;
_keyId1:int;
_keyId2:int;
@@ -406,6 +435,18 @@ export class ProtoElementInjector {
_keyId7:int;
_keyId8:int;
_keyId9:int;
+
+ _visibility0:number;
+ _visibility1:number;
+ _visibility2:number;
+ _visibility3:number;
+ _visibility4:number;
+ _visibility5:number;
+ _visibility6:number;
+ _visibility7:number;
+ _visibility8:number;
+ _visibility9:number;
+
parent:ProtoElementInjector;
index:int;
view:viewModule.AppView;
@@ -414,8 +455,6 @@ export class ProtoElementInjector {
eventEmitterAccessors:List>;
hostActionAccessors:List>;
- numberOfDirectives:number;
-
/** Whether the element is exported as $implicit. */
exportElement:boolean;
@@ -425,109 +464,144 @@ export class ProtoElementInjector {
/** The variable name that will be set to $implicit for the element. */
exportImplicitName:string;
- constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false, distanceToParent:number = 0) {
+ _firstBindingIsComponent:boolean;
+
+ static create(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean, distanceToParent:number) {
+ var bd = [];
+
+ ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent);
+ ProtoElementInjector._createHostInjectorBindingData(bindings, bd);
+ if (firstBindingIsComponent) {
+ ProtoElementInjector._createViewInjectorBindingData(bindings, bd);
+ }
+
+ return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent);
+ }
+
+ static _createDirectiveBindingData(bindings:List, bd:List, firstBindingIsComponent:boolean) {
+ if (firstBindingIsComponent) {
+ ListWrapper.push(bd, new BindingData(bindings[0], LIGHT_DOM_AND_SHADOW_DOM));
+ for (var i = 1; i < bindings.length; ++i) {
+ ListWrapper.push(bd, new BindingData(bindings[i], LIGHT_DOM));
+ }
+ } else {
+ ListWrapper.forEach(bindings, b => {
+ ListWrapper.push(bd, new BindingData(b, LIGHT_DOM))
+ });
+ }
+ }
+
+ static _createHostInjectorBindingData(bindings:List, bd:List) {
+ ListWrapper.forEach(bindings, b => {
+ ListWrapper.forEach(b.resolvedHostInjectables, b => {
+ ListWrapper.push(bd, new BindingData(b, LIGHT_DOM));
+ });
+ });
+ }
+
+ static _createViewInjectorBindingData(bindings:List, bd:List) {
+ ListWrapper.forEach(bindings[0].resolvedViewInjectables,
+ b => ListWrapper.push(bd, new BindingData(b, SHADOW_DOM)));
+ }
+
+ constructor(parent:ProtoElementInjector, index:int, bd:List, distanceToParent:number, firstBindingIsComponent:boolean) {
this.parent = parent;
this.index = index;
this.distanceToParent = distanceToParent;
this.exportComponent = false;
this.exportElement = false;
-
- this._binding0IsComponent = firstBindingIsComponent;
- this._binding0 = null; this._keyId0 = null;
- this._binding1 = null; this._keyId1 = null;
- this._binding2 = null; this._keyId2 = null;
- this._binding3 = null; this._keyId3 = null;
- this._binding4 = null; this._keyId4 = null;
- this._binding5 = null; this._keyId5 = null;
- this._binding6 = null; this._keyId6 = null;
- this._binding7 = null; this._keyId7 = null;
- this._binding8 = null; this._keyId8 = null;
- this._binding9 = null; this._keyId9 = null;
-
- this.numberOfDirectives = bindings.length;
- var length = bindings.length;
+ this._firstBindingIsComponent = firstBindingIsComponent;
+
+ this._binding0 = null; this._keyId0 = null; this._visibility0 = null;
+ this._binding1 = null; this._keyId1 = null; this._visibility1 = null;
+ this._binding2 = null; this._keyId2 = null; this._visibility2 = null;
+ this._binding3 = null; this._keyId3 = null; this._visibility3 = null;
+ this._binding4 = null; this._keyId4 = null; this._visibility4 = null;
+ this._binding5 = null; this._keyId5 = null; this._visibility5 = null;
+ this._binding6 = null; this._keyId6 = null; this._visibility6 = null;
+ this._binding7 = null; this._keyId7 = null; this._visibility7 = null;
+ this._binding8 = null; this._keyId8 = null; this._visibility8 = null;
+ this._binding9 = null; this._keyId9 = null; this._visibility9 = null;
+
+ var length = bd.length;
this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
this.hostActionAccessors = ListWrapper.createFixedSize(length);
if (length > 0) {
- this._binding0 = this._createBinding(bindings[0]);
- this._keyId0 = this._binding0.key.id;
- this.eventEmitterAccessors[0] = this._createEventEmitterAccessors(this._binding0);
- this.hostActionAccessors[0] = this._createHostActionAccessors(this._binding0);
+ this._binding0 = bd[0].binding;
+ this._keyId0 = bd[0].getKeyId();
+ this._visibility0 = bd[0].visibility;
+ this.eventEmitterAccessors[0] = bd[0].createEventEmitterAccessors();
+ this.hostActionAccessors[0] = bd[0].createHostActionAccessors();
}
if (length > 1) {
- this._binding1 = this._createBinding(bindings[1]);
- this._keyId1 = this._binding1.key.id;
- this.eventEmitterAccessors[1] = this._createEventEmitterAccessors(this._binding1);
- this.hostActionAccessors[1] = this._createHostActionAccessors(this._binding1);
+ this._binding1 = bd[1].binding;
+ this._keyId1 = bd[1].getKeyId();
+ this._visibility1 = bd[1].visibility;
+ this.eventEmitterAccessors[1] = bd[1].createEventEmitterAccessors();
+ this.hostActionAccessors[1] = bd[1].createHostActionAccessors();
}
if (length > 2) {
- this._binding2 = this._createBinding(bindings[2]);
- this._keyId2 = this._binding2.key.id;
- this.eventEmitterAccessors[2] = this._createEventEmitterAccessors(this._binding2);
- this.hostActionAccessors[2] = this._createHostActionAccessors(this._binding2);
+ this._binding2 = bd[2].binding;
+ this._keyId2 = bd[2].getKeyId();
+ this._visibility2 = bd[2].visibility;
+ this.eventEmitterAccessors[2] = bd[2].createEventEmitterAccessors();
+ this.hostActionAccessors[2] = bd[2].createHostActionAccessors();
}
if (length > 3) {
- this._binding3 = this._createBinding(bindings[3]);
- this._keyId3 = this._binding3.key.id;
- this.eventEmitterAccessors[3] = this._createEventEmitterAccessors(this._binding3);
- this.hostActionAccessors[3] = this._createHostActionAccessors(this._binding3);
+ this._binding3 = bd[3].binding;
+ this._keyId3 = bd[3].getKeyId();
+ this._visibility3 = bd[3].visibility;
+ this.eventEmitterAccessors[3] = bd[3].createEventEmitterAccessors();
+ this.hostActionAccessors[3] = bd[3].createHostActionAccessors();
}
if (length > 4) {
- this._binding4 = this._createBinding(bindings[4]);
- this._keyId4 = this._binding4.key.id;
- this.eventEmitterAccessors[4] = this._createEventEmitterAccessors(this._binding4);
- this.hostActionAccessors[4] = this._createHostActionAccessors(this._binding4);
+ this._binding4 = bd[4].binding;
+ this._keyId4 = bd[4].getKeyId();
+ this._visibility4 = bd[4].visibility;
+ this.eventEmitterAccessors[4] = bd[4].createEventEmitterAccessors();
+ this.hostActionAccessors[4] = bd[4].createHostActionAccessors();
}
if (length > 5) {
- this._binding5 = this._createBinding(bindings[5]);
- this._keyId5 = this._binding5.key.id;
- this.eventEmitterAccessors[5] = this._createEventEmitterAccessors(this._binding5);
- this.hostActionAccessors[5] = this._createHostActionAccessors(this._binding5);
+ this._binding5 = bd[5].binding;
+ this._keyId5 = bd[5].getKeyId();
+ this._visibility5 = bd[5].visibility;
+ this.eventEmitterAccessors[5] = bd[5].createEventEmitterAccessors();
+ this.hostActionAccessors[5] = bd[5].createHostActionAccessors();
}
if (length > 6) {
- this._binding6 = this._createBinding(bindings[6]);
- this._keyId6 = this._binding6.key.id;
- this.eventEmitterAccessors[6] = this._createEventEmitterAccessors(this._binding6);
- this.hostActionAccessors[6] = this._createHostActionAccessors(this._binding6);
+ this._binding6 = bd[6].binding;
+ this._keyId6 = bd[6].getKeyId();
+ this._visibility6 = bd[6].visibility;
+ this.eventEmitterAccessors[6] = bd[6].createEventEmitterAccessors();
+ this.hostActionAccessors[6] = bd[6].createHostActionAccessors();
}
if (length > 7) {
- this._binding7 = this._createBinding(bindings[7]);
- this._keyId7 = this._binding7.key.id;
- this.eventEmitterAccessors[7] = this._createEventEmitterAccessors(this._binding7);
- this.hostActionAccessors[7] = this._createHostActionAccessors(this._binding7);
+ this._binding7 = bd[7].binding;
+ this._keyId7 = bd[7].getKeyId();
+ this._visibility7 = bd[7].visibility;
+ this.eventEmitterAccessors[7] = bd[7].createEventEmitterAccessors();
+ this.hostActionAccessors[7] = bd[7].createHostActionAccessors();
}
if (length > 8) {
- this._binding8 = this._createBinding(bindings[8]);
- this._keyId8 = this._binding8.key.id;
- this.eventEmitterAccessors[8] = this._createEventEmitterAccessors(this._binding8);
- this.hostActionAccessors[8] = this._createHostActionAccessors(this._binding8);
+ this._binding8 = bd[8].binding;
+ this._keyId8 = bd[8].getKeyId();
+ this._visibility8 = bd[8].visibility;
+ this.eventEmitterAccessors[8] = bd[8].createEventEmitterAccessors();
+ this.hostActionAccessors[8] = bd[8].createHostActionAccessors();
}
if (length > 9) {
- this._binding9 = this._createBinding(bindings[9]);
- this._keyId9 = this._binding9.key.id;
- this.eventEmitterAccessors[9] = this._createEventEmitterAccessors(this._binding9);
- this.hostActionAccessors[9] = this._createHostActionAccessors(this._binding9);
+ this._binding9 = bd[9].binding;
+ this._keyId9 = bd[9].getKeyId();
+ this._visibility9 = bd[9].visibility;
+ this.eventEmitterAccessors[9] = bd[9].createEventEmitterAccessors();
+ this.hostActionAccessors[9] = bd[9].createHostActionAccessors();
}
if (length > 10) {
throw 'Maximum number of directives per element has been reached.';
}
}
- _createEventEmitterAccessors(b:DirectiveBinding) {
- return ListWrapper.map(b.eventEmitters, eventName =>
- new EventEmitterAccessor(eventName, reflector.getter(eventName))
- );
- }
-
- _createHostActionAccessors(b:DirectiveBinding) {
- var res = [];
- MapWrapper.forEach(b.hostActions, (actionExpression, actionName) => {
- ListWrapper.push(res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))
- });
- return res;
- }
-
instantiate(parent:ElementInjector):ElementInjector {
return new ElementInjector(this, parent);
}
@@ -536,20 +610,11 @@ export class ProtoElementInjector {
return this.distanceToParent < 2 ? this.parent : null;
}
- _createBinding(bindingOrType) {
- if (bindingOrType instanceof DirectiveBinding) {
- return bindingOrType;
- } else {
- var b = bind(bindingOrType).toClass(bindingOrType);
- return DirectiveBinding.createFromBinding(b, null);
- }
- }
-
get hasBindings():boolean {
return isPresent(this._binding0);
}
- getDirectiveBindingAtIndex(index:int) {
+ getBindingAtIndex(index:int) {
if (index == 0) return this._binding0;
if (index == 1) return this._binding1;
if (index == 2) return this._binding2;
@@ -597,7 +662,7 @@ export class ElementInjector extends TreeNode {
super(parent);
this._proto = proto;
- //we cannot call clearDirectives because fields won't be detected
+ //we cannot call dehydrate because fields won't be detected
this._preBuiltObjects = null;
this._lightDomAppInjector = null;
this._shadowDomAppInjector = null;
@@ -617,7 +682,7 @@ export class ElementInjector extends TreeNode {
this._buildQueries();
}
- clearDirectives() {
+ dehydrate() {
this._host = null;
this._preBuiltObjects = null;
this._lightDomAppInjector = null;
@@ -625,16 +690,16 @@ export class ElementInjector extends TreeNode {
var p = this._proto;
- if (isPresent(p._binding0) && p._binding0.callOnDestroy) {this._obj0.onDestroy();}
- if (isPresent(p._binding1) && p._binding1.callOnDestroy) {this._obj1.onDestroy();}
- if (isPresent(p._binding2) && p._binding2.callOnDestroy) {this._obj2.onDestroy();}
- if (isPresent(p._binding3) && p._binding3.callOnDestroy) {this._obj3.onDestroy();}
- if (isPresent(p._binding4) && p._binding4.callOnDestroy) {this._obj4.onDestroy();}
- if (isPresent(p._binding5) && p._binding5.callOnDestroy) {this._obj5.onDestroy();}
- if (isPresent(p._binding6) && p._binding6.callOnDestroy) {this._obj6.onDestroy();}
- if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
- if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
- if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
+ if (p._binding0 instanceof DirectiveBinding && p._binding0.callOnDestroy) {this._obj0.onDestroy();}
+ if (p._binding1 instanceof DirectiveBinding && p._binding1.callOnDestroy) {this._obj1.onDestroy();}
+ if (p._binding2 instanceof DirectiveBinding && p._binding2.callOnDestroy) {this._obj2.onDestroy();}
+ if (p._binding3 instanceof DirectiveBinding && p._binding3.callOnDestroy) {this._obj3.onDestroy();}
+ if (p._binding4 instanceof DirectiveBinding && p._binding4.callOnDestroy) {this._obj4.onDestroy();}
+ if (p._binding5 instanceof DirectiveBinding && p._binding5.callOnDestroy) {this._obj5.onDestroy();}
+ if (p._binding6 instanceof DirectiveBinding && p._binding6.callOnDestroy) {this._obj6.onDestroy();}
+ if (p._binding7 instanceof DirectiveBinding && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
+ if (p._binding8 instanceof DirectiveBinding && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
+ if (p._binding9 instanceof DirectiveBinding && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
if (isPresent(this._dynamicallyCreatedComponentBinding) && this._dynamicallyCreatedComponentBinding.callOnDestroy) {
this._dynamicallyCreatedComponent.onDestroy();
}
@@ -655,45 +720,38 @@ export class ElementInjector extends TreeNode {
this._constructionCounter = 0;
}
- instantiateDirectives(
- lightDomAppInjector:Injector,
- host:ElementInjector,
- preBuiltObjects:PreBuiltObjects) {
- var shadowDomAppInjector = null;
- if (this._proto._binding0IsComponent) {
- shadowDomAppInjector = this._createShadowDomAppInjector(this._proto._binding0, lightDomAppInjector);
- }
- this._host = host;
- this._checkShadowDomAppInjector(shadowDomAppInjector);
-
- this._preBuiltObjects = preBuiltObjects;
- this._lightDomAppInjector = lightDomAppInjector;
- this._shadowDomAppInjector = shadowDomAppInjector;
+ hydrate(injector:Injector, host:ElementInjector, preBuiltObjects:PreBuiltObjects) {
var p = this._proto;
- if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0);
- if (isPresent(p._keyId1)) this._getDirectiveByKeyId(p._keyId1);
- if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2);
- if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3);
- if (isPresent(p._keyId4)) this._getDirectiveByKeyId(p._keyId4);
- if (isPresent(p._keyId5)) this._getDirectiveByKeyId(p._keyId5);
- if (isPresent(p._keyId6)) this._getDirectiveByKeyId(p._keyId6);
- if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
- if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
- if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
+
+ this._host = host;
+ this._lightDomAppInjector = injector;
+ this._preBuiltObjects = preBuiltObjects;
+
+ if (p._firstBindingIsComponent) {
+ this._shadowDomAppInjector = this._createShadowDomAppInjector(p._binding0, injector);
+ }
+
+ this._checkShadowDomAppInjector(this._shadowDomAppInjector);
+
+ if (isPresent(p._keyId0)) this._getObjByKeyId(p._keyId0, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId1)) this._getObjByKeyId(p._keyId1, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId2)) this._getObjByKeyId(p._keyId2, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId3)) this._getObjByKeyId(p._keyId3, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId4)) this._getObjByKeyId(p._keyId4, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId5)) this._getObjByKeyId(p._keyId5, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId6)) this._getObjByKeyId(p._keyId6, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId7)) this._getObjByKeyId(p._keyId7, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId8)) this._getObjByKeyId(p._keyId8, LIGHT_DOM_AND_SHADOW_DOM);
+ if (isPresent(p._keyId9)) this._getObjByKeyId(p._keyId9, LIGHT_DOM_AND_SHADOW_DOM);
}
_createShadowDomAppInjector(componentDirective:DirectiveBinding, appInjector:Injector) {
- var shadowDomAppInjector = null;
-
- // shadowDomAppInjector
- var injectables = componentDirective.resolvedInjectables;
- if (isPresent(injectables)) {
- shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
+ if (! ListWrapper.isEmpty(componentDirective.resolvedAppInjectables)) {
+ return appInjector.createChildFromResolved(componentDirective.resolvedAppInjectables);
} else {
- shadowDomAppInjector = appInjector;
+ return appInjector;
}
- return shadowDomAppInjector;
}
dynamicallyCreateComponent(componentDirective:DirectiveBinding, parentInjector:Injector) {
@@ -704,9 +762,9 @@ export class ElementInjector extends TreeNode {
}
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
- if (this._proto._binding0IsComponent && isBlank(shadowDomAppInjector)) {
+ if (this._proto._firstBindingIsComponent && isBlank(shadowDomAppInjector)) {
throw new BaseException('A shadowDomAppInjector is required as this ElementInjector contains a component');
- } else if (!this._proto._binding0IsComponent && isPresent(shadowDomAppInjector)) {
+ } else if (!this._proto._firstBindingIsComponent && isPresent(shadowDomAppInjector)) {
throw new BaseException('No shadowDomAppInjector allowed as there is not component stored in this ElementInjector');
}
}
@@ -716,7 +774,7 @@ export class ElementInjector extends TreeNode {
return this._dynamicallyCreatedComponent;
}
- return this._getByKey(Key.get(token), 0, false, null);
+ return this._getByKey(Key.get(token), new Self(), false, null);
}
_isDynamicallyLoadedComponent(token) {
@@ -725,7 +783,7 @@ export class ElementInjector extends TreeNode {
}
hasDirective(type:Type):boolean {
- return this._getDirectiveByKeyId(Key.get(type).id) !== _undefined;
+ return this._getObjByKeyId(Key.get(type).id, LIGHT_DOM_AND_SHADOW_DOM) !== _undefined;
}
getEventEmitterAccessors() {
@@ -737,11 +795,7 @@ export class ElementInjector extends TreeNode {
}
getComponent() {
- if (this._proto._binding0IsComponent) {
- return this._obj0;
- } else {
- throw new BaseException('There is no component stored in this ElementInjector');
- }
+ return this._obj0;
}
getElementRef() {
@@ -761,7 +815,7 @@ export class ElementInjector extends TreeNode {
}
_isComponentKey(key:Key) {
- return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
+ return this._proto._firstBindingIsComponent && key.id === this._proto._keyId0;
}
_isDynamicallyLoadedComponentKey(key:Key) {
@@ -839,7 +893,7 @@ export class ElementInjector extends TreeNode {
}
return new ProtoViewRef(this._preBuiltObjects.protoView);
}
- return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
+ return this._getByKey(dep.key, dep.visibility, dep.optional, requestor);
}
_buildAttribute(dep): string {
@@ -965,25 +1019,46 @@ export class ElementInjector extends TreeNode {
if (this._query2 == query) this._query2 = null;
}
- _getByKey(key:Key, depth:number, optional:boolean, requestor:Key) {
+ _getByKey(key:Key, visibility:Visibility, optional:boolean, requestor:Key) {
var ei = this;
- if (! this._shouldIncludeSelf(depth)) {
- depth -= ei._proto.distanceToParent;
- ei = ei._parent;
- }
+ var currentVisibility = LIGHT_DOM;
+ var depth = visibility.depth;
+
+ if (! visibility.shouldIncludeSelf()) {
+ depth -= ei._proto.distanceToParent;
+
+ if (isPresent(ei._parent)) {
+ ei = ei._parent;
+ } else {
+ ei = ei._host;
+ if (!visibility.crossComponentBoundaries) {
+ currentVisibility = SHADOW_DOM;
+ }
+ }
+ }
+
while (ei != null && depth >= 0) {
var preBuiltObj = ei._getPreBuiltObjectByKeyId(key.id);
if (preBuiltObj !== _undefined) return preBuiltObj;
- var dir = ei._getDirectiveByKeyId(key.id);
+ var dir = ei._getObjByKeyId(key.id, currentVisibility);
if (dir !== _undefined) return dir;
depth -= ei._proto.distanceToParent;
- ei = ei._parent;
+
+ if (currentVisibility === SHADOW_DOM) break;
+
+ if (isPresent(ei._parent)) {
+ ei = ei._parent;
+ } else {
+ ei = ei._host;
+ if (!visibility.crossComponentBoundaries) {
+ currentVisibility = SHADOW_DOM;
+ }
+ }
}
-
if (isPresent(this._host) && this._host._isComponentKey(key)) {
return this._host.getComponent();
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
@@ -994,7 +1069,7 @@ export class ElementInjector extends TreeNode {
return this._appInjector(requestor).get(key);
}
}
-
+
_appInjector(requestor:Key) {
if (isPresent(requestor) && (this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
return this._shadowDomAppInjector;
@@ -1003,10 +1078,6 @@ export class ElementInjector extends TreeNode {
}
}
- _shouldIncludeSelf(depth:int) {
- return depth === 0;
- }
-
_getPreBuiltObjectByKeyId(keyId:int) {
var staticKeys = StaticKeys.instance();
if (keyId === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager;
@@ -1015,19 +1086,20 @@ export class ElementInjector extends TreeNode {
return _undefined;
}
- _getDirectiveByKeyId(keyId:int) {
+ _getObjByKeyId(keyId:int, visibility:number) {
var p = this._proto;
- if (p._keyId0 === keyId) {if (isBlank(this._obj0)){this._obj0 = this._new(p._binding0);} return this._obj0;}
- if (p._keyId1 === keyId) {if (isBlank(this._obj1)){this._obj1 = this._new(p._binding1);} return this._obj1;}
- if (p._keyId2 === keyId) {if (isBlank(this._obj2)){this._obj2 = this._new(p._binding2);} return this._obj2;}
- if (p._keyId3 === keyId) {if (isBlank(this._obj3)){this._obj3 = this._new(p._binding3);} return this._obj3;}
- if (p._keyId4 === keyId) {if (isBlank(this._obj4)){this._obj4 = this._new(p._binding4);} return this._obj4;}
- if (p._keyId5 === keyId) {if (isBlank(this._obj5)){this._obj5 = this._new(p._binding5);} return this._obj5;}
- if (p._keyId6 === keyId) {if (isBlank(this._obj6)){this._obj6 = this._new(p._binding6);} return this._obj6;}
- if (p._keyId7 === keyId) {if (isBlank(this._obj7)){this._obj7 = this._new(p._binding7);} return this._obj7;}
- if (p._keyId8 === keyId) {if (isBlank(this._obj8)){this._obj8 = this._new(p._binding8);} return this._obj8;}
- if (p._keyId9 === keyId) {if (isBlank(this._obj9)){this._obj9 = this._new(p._binding9);} return this._obj9;}
+ if (p._keyId0 === keyId && (p._visibility0 & visibility) > 0) {if (isBlank(this._obj0)){this._obj0 = this._new(p._binding0);} return this._obj0;}
+ if (p._keyId1 === keyId && (p._visibility1 & visibility) > 0) {if (isBlank(this._obj1)){this._obj1 = this._new(p._binding1);} return this._obj1;}
+ if (p._keyId2 === keyId && (p._visibility2 & visibility) > 0) {if (isBlank(this._obj2)){this._obj2 = this._new(p._binding2);} return this._obj2;}
+ if (p._keyId3 === keyId && (p._visibility3 & visibility) > 0) {if (isBlank(this._obj3)){this._obj3 = this._new(p._binding3);} return this._obj3;}
+ if (p._keyId4 === keyId && (p._visibility4 & visibility) > 0) {if (isBlank(this._obj4)){this._obj4 = this._new(p._binding4);} return this._obj4;}
+ if (p._keyId5 === keyId && (p._visibility5 & visibility) > 0) {if (isBlank(this._obj5)){this._obj5 = this._new(p._binding5);} return this._obj5;}
+ if (p._keyId6 === keyId && (p._visibility6 & visibility) > 0) {if (isBlank(this._obj6)){this._obj6 = this._new(p._binding6);} return this._obj6;}
+ if (p._keyId7 === keyId && (p._visibility7 & visibility) > 0) {if (isBlank(this._obj7)){this._obj7 = this._new(p._binding7);} return this._obj7;}
+ if (p._keyId8 === keyId && (p._visibility8 & visibility) > 0) {if (isBlank(this._obj8)){this._obj8 = this._new(p._binding8);} return this._obj8;}
+ if (p._keyId9 === keyId && (p._visibility9 & visibility) > 0) {if (isBlank(this._obj9)){this._obj9 = this._new(p._binding9);} return this._obj9;}
+
return _undefined;
}
diff --git a/modules/angular2/src/core/compiler/proto_view_factory.js b/modules/angular2/src/core/compiler/proto_view_factory.js
index f22d252202..373410c60e 100644
--- a/modules/angular2/src/core/compiler/proto_view_factory.js
+++ b/modules/angular2/src/core/compiler/proto_view_factory.js
@@ -319,7 +319,7 @@ function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderE
// so that, when hydrating, $implicit can be set to the element.
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
if (directiveBindings.length > 0 || hasVariables) {
- protoElementInjector = new ProtoElementInjector(
+ protoElementInjector = ProtoElementInjector.create(
parentPeiWithDistance.protoElementInjector, binderIndex,
directiveBindings,
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance
diff --git a/modules/angular2/src/core/compiler/view_manager_utils.js b/modules/angular2/src/core/compiler/view_manager_utils.js
index 263d11e9eb..2c1860c608 100644
--- a/modules/angular2/src/core/compiler/view_manager_utils.js
+++ b/modules/angular2/src/core/compiler/view_manager_utils.js
@@ -189,7 +189,7 @@ export class AppViewManagerUtils {
for (var i = 0; i < binders.length; ++i) {
var elementInjector = view.elementInjectors[i];
if (isPresent(elementInjector)) {
- elementInjector.instantiateDirectives(appInjector, hostElementInjector, view.preBuiltObjects[i]);
+ elementInjector.hydrate(appInjector, hostElementInjector, view.preBuiltObjects[i]);
this._setUpEventEmitters(view, elementInjector, i);
this._setUpHostActions(view, elementInjector, i);
@@ -238,7 +238,7 @@ export class AppViewManagerUtils {
for (var i = 0; i < binders.length; ++i) {
var elementInjector = view.elementInjectors[i];
if (isPresent(elementInjector)) {
- elementInjector.clearDirectives();
+ elementInjector.dehydrate();
}
}
if (isPresent(view.locals)) {
diff --git a/modules/angular2/src/di/injector.ts b/modules/angular2/src/di/injector.ts
index 99b13a4be5..c1d5d5be40 100644
--- a/modules/angular2/src/di/injector.ts
+++ b/modules/angular2/src/di/injector.ts
@@ -91,7 +91,7 @@ export class Injector {
* `fromResolvedBindings` and `createChildFromResolved`.
*/
static resolve(bindings: List): List {
- var resolvedBindings = _resolveBindings(bindings);
+ var resolvedBindings = resolveBindings(bindings);
var flatten = _flattenBindings(resolvedBindings, MapWrapper.create());
return _createListOfBindings(flatten);
}
@@ -368,7 +368,7 @@ class _AsyncInjectorStrategy {
}
}
-function _resolveBindings(bindings: List): List {
+export function resolveBindings(bindings: List): List {
var resolvedList = ListWrapper.createFixedSize(bindings.length);
for (var i = 0; i < bindings.length; i++) {
var unresolved = resolveForwardRef(bindings[i]);
@@ -380,7 +380,7 @@ function _resolveBindings(bindings: List): List {
} else if (unresolved instanceof Binding) {
resolved = unresolved.resolve();
} else if (unresolved instanceof List) {
- resolved = _resolveBindings(unresolved);
+ resolved = resolveBindings(unresolved);
} else if (unresolved instanceof BindingBuilder) {
throw new InvalidBindingError(unresolved.token);
} else {
@@ -391,6 +391,13 @@ function _resolveBindings(bindings: List): List {
return resolvedList;
}
+function flattenBindings(bindings: List): List {
+ var map = _flattenBindings(bindings, MapWrapper.create());
+ var res = ListWrapper.create();
+ MapWrapper.forEach(map, (binding, keyId) => ListWrapper.push(res, binding));
+ return res;
+}
+
function _createListOfBindings(flattenedBindings): List {
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js
index aeb62180da..0c60b63d46 100644
--- a/modules/angular2/test/core/compiler/element_injector_spec.js
+++ b/modules/angular2/test/core/compiler/element_injector_spec.js
@@ -2,11 +2,11 @@ import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, SpyObj
import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, List, StringMapWrapper, iterateListLike} from 'angular2/src/facade/collection';
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode}
- from 'angular2/src/core/compiler/element_injector';
-import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
+ from 'angular2/src/core/compiler/element_injector';
+import {Parent, Ancestor, Unbounded} from 'angular2/src/core/annotations_impl/visibility';
import {Attribute, Query} from 'angular2/src/core/annotations_impl/di';
import {Component, Directive, onDestroy} from 'angular2/src/core/annotations_impl/annotations';
-import {bind, Injector} from 'angular2/di';
+import {bind, Injector, Binding} from 'angular2/di';
import {Optional, Inject} from 'angular2/src/di/annotations_impl';
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
@@ -16,8 +16,10 @@ import {DynamicChangeDetector, ChangeDetectorRef, Parser, Lexer} from 'angular2/
import {ViewRef, Renderer} from 'angular2/src/render/api';
import {QueryList} from 'angular2/src/core/compiler/query_list';
-class DummyDirective extends Directive {
- constructor({lifecycle, events, hostActions} = {}) { super({lifecycle: lifecycle, events: events, hostActions:hostActions}); }
+class DummyDirective extends Component {
+ constructor({lifecycle, events, hostActions, hostInjector, viewInjector} = {}) {
+ super({lifecycle: lifecycle, events: events, hostActions: hostActions, hostInjector: hostInjector, viewInjector: viewInjector});
+ }
}
@proxy
@@ -37,6 +39,9 @@ class DummyView extends SpyObject {
class SimpleDirective {
}
+class SimpleService {
+}
+
class SomeOtherDirective {
}
@@ -69,20 +74,27 @@ class OptionallyNeedsDirective {
}
}
-class NeedDirectiveFromParent {
+class NeedsDirectiveFromParent {
dependency:SimpleDirective;
constructor(@Parent() dependency:SimpleDirective){
this.dependency = dependency;
}
}
-class NeedDirectiveFromAncestor {
+class NeedsDirectiveFromAncestor {
dependency:SimpleDirective;
constructor(@Ancestor() dependency:SimpleDirective){
this.dependency = dependency;
}
}
+class NeedsDirectiveFromAnAncestorShadowDom {
+ dependency:SimpleDirective;
+ constructor(@Unbounded() dependency:SimpleDirective){
+ this.dependency = dependency;
+ }
+}
+
class NeedsService {
service:any;
constructor(@Inject("service") service) {
@@ -199,10 +211,19 @@ export function main() {
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null);
var appInjector = Injector.resolveAndCreate([]);
+ function createPei(parent, index, bindings, distance = 1, hasShadowRoot = false) {
+ var directiveBinding = ListWrapper.map(bindings, b => {
+ if (b instanceof DirectiveBinding) return b;
+ if (b instanceof Binding) return DirectiveBinding.createFromBinding(b, null);
+ return DirectiveBinding.createFromType(b, null);
+ });
+ return ProtoElementInjector.create(parent, index, directiveBinding, hasShadowRoot, distance);
+ }
+
function humanize(tree, names:List) {
var lookupName = (item) =>
- ListWrapper.last(
- ListWrapper.find(names, (pair) => pair[0] === item));
+ ListWrapper.last(
+ ListWrapper.find(names, (pair) => pair[0] === item));
if (tree.children.length == 0) return lookupName(tree);
var children = tree.children.map(m => humanize(m, names));
@@ -210,56 +231,46 @@ export function main() {
}
function injector(bindings, lightDomAppInjector = null,
- isComponent:bool = false, preBuiltObjects = null, attributes = null) {
+ isComponent:bool = false, preBuiltObjects = null, attributes = null) {
if (isBlank(lightDomAppInjector)) lightDomAppInjector = appInjector;
- var proto = new ProtoElementInjector(null, 0, bindings, isComponent);
+ var proto = createPei(null, 0, bindings, 0, isComponent);
proto.attributes = attributes;
- var inj = proto.instantiate(null);
- var preBuilt = isPresent(preBuiltObjects)
- ? preBuiltObjects
- : defaultPreBuiltObjects;
- inj.instantiateDirectives(lightDomAppInjector, null, preBuilt);
+ var inj = proto.instantiate(null);
+ var preBuilt = isPresent(preBuiltObjects) ? preBuiltObjects : defaultPreBuiltObjects;
+ inj.hydrate(lightDomAppInjector, null, preBuilt);
return inj;
}
- function parentChildInjectors(
- parentBindings,
- childBindings,
- parentPreBuildObjects = null,
- isParentComponent:bool = false) {
+ function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null) {
if (isBlank(parentPreBuildObjects)) parentPreBuildObjects = defaultPreBuiltObjects;
var inj = Injector.resolveAndCreate([]);
- var protoParent = new ProtoElementInjector(null, 0, parentBindings, isParentComponent);
+
+ var protoParent = createPei(null, 0, parentBindings);
var parent = protoParent.instantiate(null);
- parent.instantiateDirectives(inj, null, parentPreBuildObjects);
+ parent.hydrate(inj, null, parentPreBuildObjects);
- var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
+ var protoChild = createPei(protoParent, 1, childBindings, 1, false);
var child = protoChild.instantiate(parent);
- child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
+ child.hydrate(inj, null, defaultPreBuiltObjects);
return child;
}
- function hostShadowInjectors(
- hostBindings:List,
- shadowBindings:List,
- isParentComponent:bool = true,
- isChildComponent:bool = false):ElementInjector {
+ function hostShadowInjectors(hostBindings:List, shadowBindings:List):ElementInjector {
var inj = Injector.resolveAndCreate([]);
- var protoParent = new ProtoElementInjector(null, 0, hostBindings, isParentComponent);
- var host = protoParent.instantiate(null);
- host.instantiateDirectives(inj, null, defaultPreBuiltObjects);
+ var protoHost = createPei(null, 0, hostBindings, 0, true);
+ var host = protoHost.instantiate(null);
+ host.hydrate(inj, null, defaultPreBuiltObjects);
- var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings,
- isChildComponent, 1);
- var shadow = protoChild.instantiate(null);
- shadow.instantiateDirectives(host.getShadowDomAppInjector(), host, null);
+ var protoShadow = createPei(null, 0, shadowBindings, 0, false);
+ var shadow = protoShadow.instantiate(null);
+ shadow.hydrate(host.getShadowDomAppInjector(), host, null);
return shadow;
}
@@ -268,13 +279,13 @@ export function main() {
var root, firstParent, lastParent, node;
/*
- Build a tree of the following shape:
- root
- - p1
- - c1
- - c2
- - p2
- - c3
+ Build a tree of the following shape:
+ root
+ - p1
+ - c1
+ - c2
+ - p2
+ - c3
*/
beforeEach(() => {
root = new TestNode(null, 'root');
@@ -339,43 +350,45 @@ export function main() {
});
});
+
+
describe("ProtoElementInjector", () => {
describe("direct parent", () => {
it("should return parent proto injector when distance is 1", () => {
var distance = 1;
- var protoParent = new ProtoElementInjector(null, 0, []);
- var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
+ var protoParent = createPei(null, 0, []);
+ var protoChild = createPei(protoParent, 0, [], distance, false);
expect(protoChild.directParent()).toEqual(protoParent);
});
it("should return null otherwise", () => {
var distance = 2;
- var protoParent = new ProtoElementInjector(null, 0, []);
- var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
+ var protoParent = createPei(null, 0, []);
+ var protoChild = createPei(protoParent, 0, [], distance, false);
expect(protoChild.directParent()).toEqual(null);
});
- it("should allow for direct access using getDirectiveBindingAtIndex", function () {
+ it("should allow for direct access using getBindingAtIndex", function () {
var binding = DirectiveBinding.createFromBinding(
- bind(SimpleDirective).toClass(SimpleDirective), null);
- var proto = new ProtoElementInjector(null, 0, [binding]);
+ bind(SimpleDirective).toClass(SimpleDirective), null);
+ var proto = createPei(null, 0, [binding]);
- expect(proto.getDirectiveBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding);
- expect(() => proto.getDirectiveBindingAtIndex(-1)).toThrowError(
- 'Index -1 is out-of-bounds.');
- expect(() => proto.getDirectiveBindingAtIndex(10)).toThrowError(
- 'Index 10 is out-of-bounds.');
+ expect(proto.getBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding);
+ expect(() => proto.getBindingAtIndex(-1)).toThrowError(
+ 'Index -1 is out-of-bounds.');
+ expect(() => proto.getBindingAtIndex(10)).toThrowError(
+ 'Index 10 is out-of-bounds.');
});
});
describe('event emitters', () => {
it('should return a list of event accessors', () => {
var binding = DirectiveBinding.createFromType(
- HasEventEmitter, new DummyDirective({events: ['emitter']}));
+ HasEventEmitter, new DummyDirective({events: ['emitter']}));
- var inj = new ProtoElementInjector(null, 0, [binding]);
+ var inj = createPei(null, 0, [binding]);
expect(inj.eventEmitterAccessors.length).toEqual(1);
var accessor = inj.eventEmitterAccessors[0][0];
@@ -385,9 +398,9 @@ export function main() {
it('should return a list of hostAction accessors', () => {
var binding = DirectiveBinding.createFromType(
- HasEventEmitter, new DummyDirective({hostActions: {'hostActionName' : 'onAction'}}));
+ HasEventEmitter, new DummyDirective({hostActions: {'hostActionName' : 'onAction'}}));
- var inj = new ProtoElementInjector(null, 0, [binding]);
+ var inj = createPei(null, 0, [binding]);
expect(inj.hostActionAccessors.length).toEqual(1);
var accessor = inj.hostActionAccessors[0][0];
@@ -395,14 +408,41 @@ export function main() {
expect(accessor.getter(new HasHostAction())).toEqual('hostAction');
});
});
+
+
+ describe(".create", () => {
+ it("should collect hostInjector injectables from all directives", () => {
+ var pei = createPei(null, 0, [
+ DirectiveBinding.createFromType(SimpleDirective,
+ new DummyDirective({hostInjector: [bind('injectable1').toValue('injectable1')]})),
+ DirectiveBinding.createFromType(SomeOtherDirective,
+ new DummyDirective({hostInjector: [bind('injectable2').toValue('injectable2')]}))
+ ]);
+
+ expect(pei.getBindingAtIndex(0).key.token).toBe(SimpleDirective);
+ expect(pei.getBindingAtIndex(1).key.token).toBe(SomeOtherDirective);
+ expect(pei.getBindingAtIndex(2).key.token).toEqual("injectable1");
+ expect(pei.getBindingAtIndex(3).key.token).toEqual("injectable2");
+ });
+
+ it("should collect viewInjector injectables from the component", () => {
+ var pei = createPei(null, 0, [
+ DirectiveBinding.createFromType(SimpleDirective,
+ new DummyDirective({viewInjector: [bind('injectable1').toValue('injectable1')]}))
+ ], 0, true);
+
+ expect(pei.getBindingAtIndex(0).key.token).toBe(SimpleDirective);
+ expect(pei.getBindingAtIndex(1).key.token).toEqual("injectable1");
+ });
+ });
});
describe("ElementInjector", function () {
describe("instantiate", function () {
it("should create an element injector", function () {
- var protoParent = new ProtoElementInjector(null, 0, []);
- var protoChild1 = new ProtoElementInjector(protoParent, 1, []);
- var protoChild2 = new ProtoElementInjector(protoParent, 2, []);
+ var protoParent = createPei(null, 0, []);
+ var protoChild1 = createPei(protoParent, 1, []);
+ var protoChild2 = createPei(protoParent, 2, []);
var p = protoParent.instantiate(null);
var c1 = protoChild1.instantiate(p);
@@ -418,8 +458,8 @@ export function main() {
describe("direct parent", () => {
it("should return parent injector when distance is 1", () => {
var distance = 1;
- var protoParent = new ProtoElementInjector(null, 0, []);
- var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
+ var protoParent = createPei(null, 0, []);
+ var protoChild = createPei(protoParent, 1, [], distance);
var p = protoParent.instantiate(null);
var c = protoChild.instantiate(p);
@@ -429,8 +469,8 @@ export function main() {
it("should return null otherwise", () => {
var distance = 2;
- var protoParent = new ProtoElementInjector(null, 0, []);
- var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
+ var protoParent = createPei(null, 0, []);
+ var protoChild = createPei(protoParent, 1, [], distance);
var p = protoParent.instantiate(null);
var c = protoChild.instantiate(p);
@@ -442,12 +482,12 @@ export function main() {
describe("hasBindings", function () {
it("should be true when there are bindings", function () {
- var p = new ProtoElementInjector(null, 0, [SimpleDirective]);
+ var p = createPei(null, 0, [SimpleDirective]);
expect(p.hasBindings).toBeTruthy();
});
it("should be false otherwise", function () {
- var p = new ProtoElementInjector(null, 0, []);
+ var p = createPei(null, 0, []);
expect(p.hasBindings).toBeFalsy();
});
});
@@ -462,7 +502,7 @@ export function main() {
});
});
- describe("instantiateDirectives", function () {
+ describe("hydrate", function () {
it("should instantiate directives that have no dependencies", function () {
var inj = injector([SimpleDirective]);
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
@@ -495,52 +535,6 @@ export function main() {
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
});
- it("should instantiate directives that depend on the containing component", function () {
- var directiveBinding = DirectiveBinding.createFromType(SimpleDirective, new Component());
- var shadow = hostShadowInjectors([directiveBinding], [NeedsDirective]);
-
- var d = shadow.get(NeedsDirective);
- expect(d).toBeAnInstanceOf(NeedsDirective);
- expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
- });
-
- it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", () => {
- var directiveBinding = DirectiveBinding
- .createFromType(SomeOtherDirective, new Component());
- expect(() => {
- hostShadowInjectors(
- [directiveBinding, SimpleDirective],
- [NeedsDirective]);
- }).toThrowError('No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)');
- });
-
- it("should instantiate component directives that depend on app services in the shadow app injector", () => {
- var directiveAnnotation = new Component({
- appInjector: [
- bind("service").toValue("service")
- ]
- });
- var componentDirective = DirectiveBinding.createFromType(
- NeedsService, directiveAnnotation);
- var inj = injector([componentDirective], null, true);
-
- var d = inj.get(NeedsService);
- expect(d).toBeAnInstanceOf(NeedsService);
- expect(d.service).toEqual("service");
- });
-
- it("should not instantiate other directives that depend on app services in the shadow app injector", () => {
- var directiveAnnotation = new Component({
- appInjector: [
- bind("service").toValue("service")
- ]
- });
- var componentDirective = DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation);
- expect(() => {
- injector([componentDirective, NeedsService], null);
- }).toThrowError('No provider for service! (NeedsService -> service)');
- });
-
it("should return app services", function () {
var appInjector = Injector.resolveAndCreate([
bind("service").toValue("service")
@@ -551,41 +545,51 @@ export function main() {
});
it("should get directives from parent", function () {
- var child = parentChildInjectors([SimpleDirective], [NeedDirectiveFromParent]);
+ var child = parentChildInjectors([SimpleDirective], [NeedsDirectiveFromParent]);
- var d = child.get(NeedDirectiveFromParent);
+ var d = child.get(NeedsDirectiveFromParent);
- expect(d).toBeAnInstanceOf(NeedDirectiveFromParent);
+ expect(d).toBeAnInstanceOf(NeedsDirectiveFromParent);
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
});
it("should not return parent's directives on self", function () {
expect(() => {
- injector([SimpleDirective, NeedDirectiveFromParent]);
+ injector([SimpleDirective, NeedsDirectiveFromParent]);
}).toThrowError(new RegExp("No provider for SimpleDirective"));
});
it("should get directives from ancestor", function () {
- var child = parentChildInjectors([SimpleDirective], [NeedDirectiveFromAncestor]);
+ var child = parentChildInjectors([SimpleDirective], [NeedsDirectiveFromAncestor]);
- var d = child.get(NeedDirectiveFromAncestor);
+ var d = child.get(NeedsDirectiveFromAncestor);
- expect(d).toBeAnInstanceOf(NeedDirectiveFromAncestor);
+ expect(d).toBeAnInstanceOf(NeedsDirectiveFromAncestor);
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
});
- it("should throw when no SimpleDirective found", function () {
- expect(() => injector([NeedDirectiveFromParent])).
- toThrowError('No provider for SimpleDirective! (NeedDirectiveFromParent -> SimpleDirective)');
+ it("should get directives crossing the boundaries", function () {
+ var child = hostShadowInjectors([SomeOtherDirective, SimpleDirective],
+ [NeedsDirectiveFromAnAncestorShadowDom]);
+
+ var d = child.get(NeedsDirectiveFromAnAncestorShadowDom);
+
+ expect(d).toBeAnInstanceOf(NeedsDirectiveFromAnAncestorShadowDom);
+ expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
});
- it("should inject null when no directive found", function () {
+ it("should throw when a depenency cannot be resolved", function () {
+ expect(() => injector([NeedsDirectiveFromParent])).
+ toThrowError('No provider for SimpleDirective! (NeedsDirectiveFromParent -> SimpleDirective)');
+ });
+
+ it("should inject null when an optional dependency cannot be resolved", function () {
var inj = injector([OptionallyNeedsDirective]);
var d = inj.get(OptionallyNeedsDirective);
expect(d.dependency).toEqual(null);
});
- it("should accept SimpleDirective bindings instead of SimpleDirective types", function () {
+ it("should accept bindings instead types", function () {
var inj = injector([
DirectiveBinding.createFromBinding(bind(SimpleDirective).toClass(SimpleDirective), null)
]);
@@ -598,11 +602,12 @@ export function main() {
]);
expect(inj.getDirectiveAtIndex(0)).toBeAnInstanceOf(SimpleDirective);
expect(() => inj.getDirectiveAtIndex(-1)).toThrowError(
- 'Index -1 is out-of-bounds.');
+ 'Index -1 is out-of-bounds.');
expect(() => inj.getDirectiveAtIndex(10)).toThrowError(
- 'Index 10 is out-of-bounds.');
+ 'Index 10 is out-of-bounds.');
});
+
it("should handle cyclic dependencies", function () {
expect(() => {
var bAneedsB = bind(A_Needs_B).toFactory((a) => new A_Needs_B(a), [B_Needs_A]);
@@ -612,56 +617,107 @@ export function main() {
DirectiveBinding.createFromBinding(bBneedsA, null)
]);
}).toThrowError('Cannot instantiate cyclic dependency! ' +
- '(A_Needs_B -> B_Needs_A -> A_Needs_B)');
+ '(A_Needs_B -> B_Needs_A -> A_Needs_B)');
});
+ describe("shadow DOM components", () => {
+ it("should instantiate directives that depend on the containing component", function () {
+ var directiveBinding = DirectiveBinding.createFromType(SimpleDirective, new Component());
+ var shadow = hostShadowInjectors([directiveBinding], [NeedsDirective]);
+
+ var d = shadow.get(NeedsDirective);
+ expect(d).toBeAnInstanceOf(NeedsDirective);
+ expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
+ });
+
+ it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", () => {
+ var directiveBinding = DirectiveBinding.createFromType(SomeOtherDirective, new Component());
+ expect(() => {
+ hostShadowInjectors([directiveBinding, SimpleDirective],[NeedsDirective]);
+ }).toThrowError('No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)');
+ });
+
+ it("should instantiate component directives that depend on app services in the shadow app injector", () => {
+ var directiveAnnotation = new Component({
+ appInjector: [
+ bind("service").toValue("service")
+ ]
+ });
+ var componentDirective = DirectiveBinding.createFromType(
+ NeedsService, directiveAnnotation);
+ var inj = injector([componentDirective], null, true);
+
+ var d = inj.get(NeedsService);
+ expect(d).toBeAnInstanceOf(NeedsService);
+ expect(d.service).toEqual("service");
+ });
+
+ it("should not instantiate other directives that depend on app services in the shadow app injector", () => {
+ var directiveAnnotation = new Component({
+ appInjector: [
+ bind("service").toValue("service")
+ ]
+ });
+ var componentDirective = DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation);
+ expect(() => {
+ injector([componentDirective, NeedsService], null);
+ }).toThrowError('No provider for service! (NeedsService -> service)');
+ });
+ });
+ });
+
+ describe("lifecycle", () => {
it("should call onDestroy on directives subscribed to this event", function() {
- var inj = injector([DirectiveBinding.createFromType(
- DirectiveWithDestroy,
- new DummyDirective({lifecycle: [onDestroy]}))]);
+ var inj = injector([DirectiveBinding.createFromType(DirectiveWithDestroy, new DummyDirective({lifecycle: [onDestroy]}))]);
var destroy = inj.get(DirectiveWithDestroy);
- inj.clearDirectives();
+ inj.dehydrate();
expect(destroy.onDestroyCounter).toBe(1);
});
+
+ it("should work with services", function() {
+ var inj = injector([DirectiveBinding.createFromType(SimpleDirective, new DummyDirective({hostInjector: [SimpleService]}))]);
+ inj.dehydrate();
+ });
});
describe("dynamicallyCreateComponent", () => {
it("should create a component dynamically", () => {
var inj = injector([]);
- inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null);
+
+ inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), appInjector);
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective);
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
});
it("should inject parent dependencies into the dynamically-loaded component", () => {
var inj = parentChildInjectors([SimpleDirective], []);
- inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedDirectiveFromAncestor, null), null);
- expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedDirectiveFromAncestor);
+ inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsDirectiveFromAncestor, null), appInjector);
+ expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedsDirectiveFromAncestor);
expect(inj.getDynamicallyLoadedComponent().dependency).toBeAnInstanceOf(SimpleDirective);
});
it("should not inject the proxy component into the children of the dynamically-loaded component", () => {
var injWithDynamicallyLoadedComponent = injector([SimpleDirective]);
- injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SomeOtherDirective, null), null);
+ injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SomeOtherDirective, null), appInjector);
- var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
+ var shadowDomProtoInjector = createPei(null, 0, [NeedsDirectiveFromAncestor]);
var shadowDomInj = shadowDomProtoInjector.instantiate(null);
expect(() =>
- shadowDomInj.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects)).
- toThrowError(new RegExp("No provider for SimpleDirective"));
+ shadowDomInj.hydrate(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects)).
+ toThrowError(new RegExp("No provider for SimpleDirective"));
});
it("should not inject the dynamically-loaded component into directives on the same element", () => {
var dynamicComp = DirectiveBinding.createFromType(SomeOtherDirective, new Component());
- var proto = new ProtoElementInjector(null, 0, [dynamicComp, NeedsDirective], true);
+ var proto = createPei(null, 0, [dynamicComp, NeedsDirective], 1, true);
var inj = proto.instantiate(null);
inj.dynamicallyCreateComponent(
- DirectiveBinding.createFromType(SimpleDirective, null), null);
+ DirectiveBinding.createFromType(SimpleDirective, null), appInjector);
var error = null;
try {
- inj.instantiateDirectives(Injector.resolveAndCreate([]), null, null);
+ inj.hydrate(Injector.resolveAndCreate([]), null, null);
} catch(e) {
error = e;
}
@@ -672,32 +728,32 @@ export function main() {
it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", () => {
var componentDirective = DirectiveBinding.createFromType(SimpleDirective, null);
var injWithDynamicallyLoadedComponent = injector([]);
- injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective, null);
+ injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective, appInjector);
- var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
+ var shadowDomProtoInjector = createPei(null, 0, [NeedsDirectiveFromAncestor]);
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
- shadowDomInjector.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects);
+ shadowDomInjector.hydrate(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects);
- expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor);
- expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
+ expect(shadowDomInjector.get(NeedsDirectiveFromAncestor)).toBeAnInstanceOf(NeedsDirectiveFromAncestor);
+ expect(shadowDomInjector.get(NeedsDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
});
it("should remove the dynamically-loaded component when dehydrating", () => {
var inj = injector([]);
inj.dynamicallyCreateComponent(
- DirectiveBinding.createFromType(
- DirectiveWithDestroy,
- new DummyDirective({lifecycle: [onDestroy]})
- ),
- null);
+ DirectiveBinding.createFromType(
+ DirectiveWithDestroy,
+ new DummyDirective({lifecycle: [onDestroy]})
+ ),
+ appInjector);
var dir = inj.getDynamicallyLoadedComponent();
- inj.clearDirectives();
+ inj.dehydrate();
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
expect(dir.onDestroyCounter).toBe(1);
- inj.instantiateDirectives(null, null, null);
+ inj.hydrate(null, null, null);
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
});
@@ -766,7 +822,7 @@ export function main() {
it("should throw if there is no ProtoViewRef", function () {
expect(
- () => injector([NeedsProtoViewRef])
+ () => injector([NeedsProtoViewRef])
).toThrowError('No provider for ProtoViewRef! (NeedsProtoViewRef -> ProtoViewRef)');
});
@@ -814,25 +870,25 @@ export function main() {
//});
it('should contain directives on the same and a child injector in construction order', () => {
- var protoParent = new ProtoElementInjector(null, 0, [NeedsQuery, CountingDirective]);
- var protoChild = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
+ var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]);
+ var protoChild = createPei(protoParent, 1, [CountingDirective]);
var parent = protoParent.instantiate(null);
var child = protoChild.instantiate(parent);
- parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
+ parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0,1]);
});
it('should reflect unlinking an injector', () => {
- var protoParent = new ProtoElementInjector(null, 0, [NeedsQuery, CountingDirective]);
- var protoChild = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
+ var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]);
+ var protoChild = createPei(protoParent, 1, [CountingDirective]);
var parent = protoParent.instantiate(null);
var child = protoChild.instantiate(parent);
- parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
+ parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
child.unlink();
@@ -840,17 +896,17 @@ export function main() {
});
it('should reflect moving an injector as a last child', () => {
- var protoParent = new ProtoElementInjector(null, 0, [NeedsQuery, CountingDirective]);
- var protoChild1 = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
- var protoChild2 = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
+ var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]);
+ var protoChild1 = createPei(protoParent, 1, [CountingDirective]);
+ var protoChild2 = createPei(protoParent, 1, [CountingDirective]);
var parent = protoParent.instantiate(null);
var child1 = protoChild1.instantiate(parent);
var child2 = protoChild2.instantiate(parent);
- parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child1.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child2.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
+ parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child1.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child2.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
child1.unlink();
child1.link(parent);
@@ -860,17 +916,17 @@ export function main() {
});
it('should reflect moving an injector as a first child', () => {
- var protoParent = new ProtoElementInjector(null, 0, [NeedsQuery, CountingDirective]);
- var protoChild1 = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
- var protoChild2 = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
+ var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]);
+ var protoChild1 = createPei(protoParent, 1, [CountingDirective]);
+ var protoChild2 = createPei(protoParent, 1, [CountingDirective]);
var parent = protoParent.instantiate(null);
var child1 = protoChild1.instantiate(parent);
var child2 = protoChild2.instantiate(parent);
- parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child1.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child2.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
+ parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child1.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child2.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
child2.unlink();
child2.linkAfter(parent, null);
@@ -880,17 +936,17 @@ export function main() {
});
it('should support two concurrent queries for the same directive', () => {
- var protoGrandParent = new ProtoElementInjector(null, 0, [NeedsQuery]);
- var protoParent = new ProtoElementInjector(null, 0, [NeedsQuery]);
- var protoChild = new ProtoElementInjector(protoParent, 1, [CountingDirective]);
+ var protoGrandParent = createPei(null, 0, [NeedsQuery]);
+ var protoParent = createPei(null, 0, [NeedsQuery]);
+ var protoChild = createPei(protoParent, 1, [CountingDirective]);
var grandParent = protoGrandParent.instantiate(null);
var parent = protoParent.instantiate(grandParent);
var child = protoChild.instantiate(parent);
- grandParent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- parent.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
- child.instantiateDirectives(Injector.resolveAndCreate([]), null, preBuildObjects);
+ grandParent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
+ child.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects);
var queryList1 = grandParent.get(NeedsQuery).query;
var queryList2 = parent.get(NeedsQuery).query;
diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js
index 9aa15376ca..bd3e44279e 100644
--- a/modules/angular2/test/core/compiler/integration_spec.js
+++ b/modules/angular2/test/core/compiler/integration_spec.js
@@ -29,7 +29,7 @@ import {PipeRegistry, defaultPipeRegistry,
import {Directive, Component} from 'angular2/src/core/annotations_impl/annotations';
import {QueryList} from 'angular2/src/core/compiler/query_list';
import {View} from 'angular2/src/core/annotations_impl/view';
-import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
+import {Parent, Ancestor, Unbounded} from 'angular2/src/core/annotations_impl/visibility';
import {Attribute, Query} from 'angular2/src/core/annotations_impl/di';
import {NgIf} from 'angular2/src/directives/ng_if';
@@ -822,6 +822,68 @@ export function main() {
}));
});
+ describe("dependency injection", () => {
+ it("should support hostInjector", inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new View({
+ template: `
+
+
+
+
+ `,
+ directives: [DirectiveProvidingInjectable, DirectiveConsumingInjectable]
+ }));
+ tb.createView(MyComp, {context: ctx}).then((view) => {
+ var comp = view.rawView.locals.get("consuming");
+ expect(comp.injectable).toBeAnInstanceOf(Injectable);
+
+ async.done();
+ });
+ }));
+
+ it("should support viewInjector", inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(DirectiveProvidingInjectableInView, new View({
+ template: `
+
+
+ `,
+ directives: [DirectiveConsumingInjectable]
+ }));
+ tb.createView(DirectiveProvidingInjectableInView, {context: new DirectiveProvidingInjectableInView()}).then((view) => {
+ var comp = view.rawView.locals.get("consuming");
+ expect(comp.injectable).toBeAnInstanceOf(Injectable);
+
+ async.done();
+ });
+ }));
+
+ it("should support unbounded lookup", inject([TestBed, AsyncTestCompleter], (tb, async) => {
+ tb.overrideView(MyComp, new View({
+ template: `
+
+
+
+
+ `,
+ directives: [DirectiveProvidingInjectable, DirectiveContainingDirectiveConsumingAnInjectable]
+ }));
+
+ tb.overrideView(DirectiveContainingDirectiveConsumingAnInjectable, new View({
+ template: `
+
+ `,
+ directives: [DirectiveConsumingInjectableUnbounded]
+ }));
+
+ tb.createView(MyComp, {context: ctx}).then((view) => {
+ var comp = view.rawView.locals.get("dir");
+ expect(comp.directive.injectable).toBeAnInstanceOf(Injectable);
+
+ async.done();
+ });
+ }));
+ });
+
describe("error handling", () => {
it('should report a meaningful error when a directive is missing annotation',
inject([TestBed, AsyncTestCompleter], (tb, async) => {
@@ -1477,3 +1539,57 @@ class DirectiveWithTwoWayBinding {
ObservableWrapper.callNext(this.control, value);
}
}
+
+class Injectable {}
+
+@Directive({
+ selector: 'directive-providing-injectable',
+ hostInjector: [Injectable]
+})
+class DirectiveProvidingInjectable {
+}
+
+@Component({
+ selector: 'directive-providing-injectable',
+ viewInjector: [Injectable]
+})
+@View({template:''})
+class DirectiveProvidingInjectableInView {
+}
+
+@Component({
+ selector: 'directive-consuming-injectable'
+})
+@View({template:''})
+class DirectiveConsumingInjectable {
+ injectable;
+
+ constructor(@Ancestor() injectable:Injectable) {
+ this.injectable = injectable;
+ }
+}
+
+
+
+@Component({
+ selector: 'directive-containing-directive-consuming-an-injectable'
+})
+class DirectiveContainingDirectiveConsumingAnInjectable {
+ directive;
+}
+
+@Component({
+ selector: 'directive-consuming-injectable-unbounded'
+})
+@View({template:''})
+class DirectiveConsumingInjectableUnbounded {
+ injectable;
+
+ constructor(
+ @Unbounded() injectable:Injectable,
+ @Ancestor() parent:DirectiveContainingDirectiveConsumingAnInjectable) {
+
+ this.injectable = injectable;
+ parent.directive = this;
+ }
+}
diff --git a/modules/angular2/test/core/compiler/view_manager_utils_spec.js b/modules/angular2/test/core/compiler/view_manager_utils_spec.js
index 80f7924cc9..42ffa7b171 100644
--- a/modules/angular2/test/core/compiler/view_manager_utils_spec.js
+++ b/modules/angular2/test/core/compiler/view_manager_utils_spec.js
@@ -131,14 +131,14 @@ export function main() {
// (() => () nonsense is required until our transpiler supports type casting
var spyEi = (() => componentView.elementInjectors[0])();
- spyEi.spy('instantiateDirectives').andCallFake(log.fn('instantiateDirectives'));
+ spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
var spyCd = (() => componentView.changeDetector)();
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
utils.hydrateComponentView(hostView, 0)
- expect(log.result()).toEqual('instantiateDirectives; hydrateCD');
+ expect(log.result()).toEqual('hydrate; hydrateCD');
});
});
@@ -261,7 +261,7 @@ export function main() {
createViews();
utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null);
- expect(childView.rootElementInjectors[0].spy('instantiateDirectives'))
+ expect(childView.rootElementInjectors[0].spy('hydrate'))
.toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(), childView.preBuiltObjects[0]);
});
@@ -282,7 +282,7 @@ export function main() {
createViews();
utils.hydrateRootHostView(hostView, injector);
- expect(hostView.rootElementInjectors[0].spy('instantiateDirectives'))
+ expect(hostView.rootElementInjectors[0].spy('hydrate'))
.toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]);
});
diff --git a/modules/angular2/test/core/forward_ref_integration_spec.es6 b/modules/angular2/test/core/forward_ref_integration_spec.es6
index c06835af89..9b802baca4 100644
--- a/modules/angular2/test/core/forward_ref_integration_spec.es6
+++ b/modules/angular2/test/core/forward_ref_integration_spec.es6
@@ -35,7 +35,7 @@ export function main() {
@Component({
selector: 'app',
- injectables: [
+ appInjector: [
forwardRef(() => Frame)
]
})
diff --git a/modules/benchmarks/e2e_test/element_injector_perf.es6 b/modules/benchmarks/e2e_test/element_injector_perf.es6
index a8eae2a5bc..4e09f7e9fc 100644
--- a/modules/benchmarks/e2e_test/element_injector_perf.es6
+++ b/modules/benchmarks/e2e_test/element_injector_perf.es6
@@ -20,11 +20,11 @@ describe('ng2 element injector benchmark', function () {
}).then(done, done.fail);
});
- it('should log the stats for instantiateDirectives', function(done) {
+ it('should log the stats for hydrate', function(done) {
perfUtil.runClickBenchmark({
url: URL,
- buttons: ['#instantiateDirectives'],
- id: 'ng2.elementInjector.instantiateDirectives',
+ buttons: ['#hydrate'],
+ id: 'ng2.elementInjector.hydrate',
params: [{
name: 'iterations', value: 20000, scale: 'linear'
}],
diff --git a/modules/benchmarks/src/element_injector/element_injector_benchmark.html b/modules/benchmarks/src/element_injector/element_injector_benchmark.html
index 9098d9a733..409d5fb7a0 100644
--- a/modules/benchmarks/src/element_injector/element_injector_benchmark.html
+++ b/modules/benchmarks/src/element_injector/element_injector_benchmark.html
@@ -13,7 +13,7 @@
Actions
-
+
$SCRIPTS$
diff --git a/modules/benchmarks/src/element_injector/element_injector_benchmark.js b/modules/benchmarks/src/element_injector/element_injector_benchmark.js
index 66517fba21..d34ec94cb7 100644
--- a/modules/benchmarks/src/element_injector/element_injector_benchmark.js
+++ b/modules/benchmarks/src/element_injector/element_injector_benchmark.js
@@ -1,7 +1,7 @@
import {reflector} from 'angular2/src/reflection/reflection';
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
import {Injectable, Injector} from 'angular2/di';
-import {ProtoElementInjector} from 'angular2/src/core/compiler/element_injector';
+import {ProtoElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {getIntParameter, bindAction, microBenchmark} from 'angular2/src/test_lib/benchmark_util';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
@@ -14,21 +14,25 @@ export function main() {
reflector.reflectionCapabilities = new ReflectionCapabilities();
var appInjector = Injector.resolveAndCreate([]);
- var bindings = [A, B, C];
- var proto = new ProtoElementInjector(null, 0, bindings);
+ var bindings = [
+ DirectiveBinding.createFromType(A, null),
+ DirectiveBinding.createFromType(B, null),
+ DirectiveBinding.createFromType(C, null)
+ ];
+ var proto = ProtoElementInjector.create(null, 0, bindings, false, 0);
var elementInjector = proto.instantiate(null);
function instantiate () {
for (var i = 0; i < iterations; ++i) {
var ei = proto.instantiate(null);
- ei.instantiateDirectives(appInjector, null, null);
+ ei.hydrate(appInjector, null, null);
}
}
- function instantiateDirectives () {
+ function hydrate () {
for (var i = 0; i < iterations; ++i) {
- elementInjector.clearDirectives();
- elementInjector.instantiateDirectives(appInjector, null, null);
+ elementInjector.dehydrate();
+ elementInjector.hydrate(appInjector, null, null);
}
}
@@ -37,8 +41,8 @@ export function main() {
() => microBenchmark('instantiateAvg', iterations, instantiate)
);
bindAction(
- '#instantiateDirectives',
- () => microBenchmark('instantiateAvg', iterations, instantiateDirectives)
+ '#hydrate',
+ () => microBenchmark('instantiateAvg', iterations, hydrate)
);
}