diff --git a/modules/angular2/src/core/compiler/directive_metadata.js b/modules/angular2/src/core/compiler/directive_metadata.js index 43ac0f8eff..688657c32b 100644 --- a/modules/angular2/src/core/compiler/directive_metadata.js +++ b/modules/angular2/src/core/compiler/directive_metadata.js @@ -1,5 +1,7 @@ import {Type} from 'angular2/src/facade/lang'; +import {List} from 'angular2/src/facade/collection'; import {Directive} from 'angular2/src/core/annotations/annotations' +import {ResolvedBinding} from 'angular2/di'; /** * Combination of a type with the Directive annotation @@ -7,9 +9,11 @@ import {Directive} from 'angular2/src/core/annotations/annotations' export class DirectiveMetadata { type:Type; annotation:Directive; + resolvedInjectables:List; - constructor(type:Type, annotation:Directive) { + constructor(type:Type, annotation:Directive, resolvedInjectables:List) { this.annotation = annotation; this.type = type; + this.resolvedInjectables = resolvedInjectables; } } diff --git a/modules/angular2/src/core/compiler/directive_metadata_reader.js b/modules/angular2/src/core/compiler/directive_metadata_reader.js index 5cb8821926..bd6f7819b5 100644 --- a/modules/angular2/src/core/compiler/directive_metadata_reader.js +++ b/modules/angular2/src/core/compiler/directive_metadata_reader.js @@ -1,6 +1,6 @@ -import {Injectable} from 'angular2/di'; +import {Injectable, Injector} from 'angular2/di'; import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang'; -import {Directive} from '../annotations/annotations'; +import {Directive, Component} from '../annotations/annotations'; import {DirectiveMetadata} from './directive_metadata'; import {reflector} from 'angular2/src/reflection/reflection'; @@ -13,7 +13,11 @@ export class DirectiveMetadataReader { var annotation = annotations[i]; if (annotation instanceof Directive) { - return new DirectiveMetadata(type, annotation); + var resolvedInjectables = null; + if (annotation instanceof Component && isPresent(annotation.injectables)) { + resolvedInjectables = Injector.resolve(annotation.injectables); + } + return new DirectiveMetadata(type, annotation, resolvedInjectables); } } } diff --git a/modules/angular2/src/core/compiler/dynamic_component_loader.js b/modules/angular2/src/core/compiler/dynamic_component_loader.js index 875da4b00e..c71154f6e0 100644 --- a/modules/angular2/src/core/compiler/dynamic_component_loader.js +++ b/modules/angular2/src/core/compiler/dynamic_component_loader.js @@ -1,7 +1,8 @@ -import {Key, Injector, Injectable} from 'angular2/di' +import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di' import {Compiler} from './compiler'; import {DirectiveMetadataReader} from './directive_metadata_reader'; import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang'; +import {List} from 'angular2/src/facade/collection'; import {Promise} from 'angular2/src/facade/async'; import {Component} from 'angular2/src/core/annotations/annotations'; import {ViewFactory} from 'angular2/src/core/compiler/view_factory'; @@ -34,15 +35,15 @@ export class DynamicComponentLoader { loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise { this._assertTypeIsComponent(type); - var annotation = this._directiveMetadataReader.read(type).annotation; + var directiveMetadata = this._directiveMetadataReader.read(type); - var inj = this._componentAppInjector(location, injector, annotation.injectables); + var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables); var hostEi = location.elementInjector; var hostView = location.hostView; return this._compiler.compile(type).then(componentProtoView => { - var context = hostEi.dynamicallyCreateComponent(type, annotation, inj); + var context = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj); var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, context); //TODO(vsavkin): do not use component child views as we need to clear the dynamically created views @@ -78,9 +79,9 @@ export class DynamicComponentLoader { }); } - _componentAppInjector(location, injector, services) { + _componentAppInjector(location, injector:Injector, resolvedBindings:List) { var inj = isPresent(injector) ? injector : location.injector; - return isPresent(services) ? inj.resolveAndCreateChild(services) : inj; + return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj; } _instantiateAndHydrateView(protoView, injector, hostElementInjector, context) { diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js index f6edc9e03e..fcd063351c 100644 --- a/modules/angular2/src/core/compiler/element_injector.js +++ b/modules/angular2/src/core/compiler/element_injector.js @@ -7,7 +7,7 @@ import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/ import * as viewModule from 'angular2/src/core/compiler/view'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {NgElement} from 'angular2/src/core/compiler/ng_element'; -import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations'; +import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations'; import {BindingPropagationConfig} from 'angular2/change_detection'; import {QueryList} from './query_list'; @@ -283,6 +283,7 @@ export class DirectiveBinding extends ResolvedBinding { callOnChange:boolean; callOnAllChangesDone:boolean; annotation:Directive; + resolvedInjectables:List; constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) { super(key, factory, dependencies, providedAsPromise); @@ -290,6 +291,9 @@ export class DirectiveBinding extends ResolvedBinding { this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange); this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone); this.annotation = annotation; + if (annotation instanceof Component && isPresent(annotation.injectables)) { + this.resolvedInjectables = Injector.resolve(annotation.injectables); + } } static createFromBinding(b:Binding, annotation:Directive):DirectiveBinding { diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index ebd7ef7ee8..d455cb11e6 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -151,9 +151,9 @@ export class AppView { // shadowDomAppInjector if (isPresent(componentDirective)) { - var injectables = componentDirective.annotation.injectables; + var injectables = componentDirective.resolvedInjectables; if (isPresent(injectables)) - shadowDomAppInjector = appInjector.resolveAndCreateChild(injectables); + shadowDomAppInjector = appInjector.createChildFromResolved(injectables); else { shadowDomAppInjector = appInjector; } diff --git a/modules/angular2/src/di/injector.js b/modules/angular2/src/di/injector.js index 7181719701..96984ae2e0 100644 --- a/modules/angular2/src/di/injector.js +++ b/modules/angular2/src/di/injector.js @@ -267,14 +267,12 @@ class _AsyncInjectorStrategy { } } -function _createListOfBindings(flattenBindings):List { +function _createListOfBindings(flattenedBindings):List { var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1); - MapWrapper.forEach(flattenBindings, (v, keyId) => bindings[keyId] = v); + MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v); return bindings; } - - function _flattenBindings(bindings:List, res:Map) { ListWrapper.forEach(bindings, function (b) { if (b instanceof ResolvedBinding) { diff --git a/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js b/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js index de1ac349a1..3f5ee82b72 100644 --- a/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js +++ b/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js @@ -1,13 +1,18 @@ +import {isPresent} from 'angular2/src/facade/lang'; +import {ListWrapper} from 'angular2/src/facade/collection'; import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations'; import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; +import {Injectable, Injector} from 'angular2/di'; +@Injectable() +class SomeInjectable {} @Decorator({selector: 'someDecorator'}) class SomeDecorator {} -@Component({selector: 'someComponent'}) +@Component({selector: 'someComponent', injectables: [SomeInjectable]}) class SomeComponent {} @Viewport({selector: 'someViewport'}) @@ -27,19 +32,30 @@ export function main() { it('should read out the Decorator annotation', () => { var directiveMetadata = reader.read(SomeDecorator); expect(directiveMetadata).toEqual( - new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'}))); + new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'}), null)); }); it('should read out the Viewport annotation', () => { var directiveMetadata = reader.read(SomeViewport); expect(directiveMetadata).toEqual( - new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'}))); + new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'}), null)); }); it('should read out the Component annotation', () => { - var directiveMetadata = reader.read(SomeComponent); - expect(directiveMetadata).toEqual( - new DirectiveMetadata(SomeComponent, new Component({selector: 'someComponent'}))); + var m = reader.read(SomeComponent); + // For some reason `toEqual` fails to compare ResolvedBinding objects. + // Have to decompose and compare. + expect(m.type).toEqual(SomeComponent); + expect(m.annotation) + .toEqual(new Component({selector: 'someComponent', injectables: [SomeInjectable]})); + var resolvedList = ListWrapper.reduce(m.resolvedInjectables, function(prev, elem) { + if (isPresent(elem)) { + ListWrapper.push(prev, elem); + } + return prev; + }, []); + expect(resolvedList.length).toBe(1); + expect(resolvedList[0].key.token).toBe(SomeInjectable); }); it('should throw if not matching annotation is found', () => { diff --git a/modules/angular2/test/di/injector_spec.js b/modules/angular2/test/di/injector_spec.js index 21d8a0a5e6..508519d148 100644 --- a/modules/angular2/test/di/injector_spec.js +++ b/modules/angular2/test/di/injector_spec.js @@ -1,5 +1,6 @@ +import {isBlank} from 'angular2/src/facade/lang'; import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib'; -import {Injector, Inject, InjectLazy, Optional, bind} from 'angular2/di'; +import {Injector, Inject, InjectLazy, Optional, bind, ResolvedBinding} from 'angular2/di'; class Engine { } @@ -364,5 +365,15 @@ export function main() { expect(e1).toBe(e2); }); }); + + describe('resolve', function() { + it('should resolve and flatten', function() { + var bindings = Injector.resolve([Engine, [BrokenEngine]]); + bindings.forEach(function(b) { + if (isBlank(b)) return; // the result is a sparse array + expect(b instanceof ResolvedBinding).toBe(true); + }); + }); + }); }); }