diff --git a/modules/angular2/src/core/annotations/annotations.js b/modules/angular2/src/core/annotations/annotations.js index 66e944c923..8b5ca342fa 100644 --- a/modules/angular2/src/core/annotations/annotations.js +++ b/modules/angular2/src/core/annotations/annotations.js @@ -245,21 +245,18 @@ export class DynamicComponent extends Directive { bind, events, services, - implementsTypes, lifecycle }:{ selector:string, - bind:any, - events:any, + bind:Object, + events:Object, services:List, - implementsTypes:List, lifecycle:List }={}) { super({ selector: selector, bind: bind, events: events, - implementsTypes: implementsTypes, lifecycle: lifecycle }); diff --git a/modules/angular2/src/core/compiler/private_component_loader.js b/modules/angular2/src/core/compiler/private_component_loader.js new file mode 100644 index 0000000000..336ac9fa5d --- /dev/null +++ b/modules/angular2/src/core/compiler/private_component_loader.js @@ -0,0 +1,34 @@ +import {Compiler} from './compiler'; +import {ShadowDomStrategy} from './shadow_dom_strategy'; +import {EventManager} from 'angular2/src/core/events/event_manager'; +import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; +import {PrivateComponentLocation} from './private_component_location'; +import {Type} from 'angular2/src/facade/lang'; + + +export class PrivateComponentLoader { + compiler:Compiler; + shadowDomStrategy:ShadowDomStrategy; + eventManager:EventManager; + directiveMetadataReader:DirectiveMetadataReader; + + constructor(compiler:Compiler, shadowDomStrategy:ShadowDomStrategy, + eventManager:EventManager, directiveMetadataReader:DirectiveMetadataReader) { + + this.compiler = compiler; + this.shadowDomStrategy = shadowDomStrategy; + this.eventManager = eventManager; + this.directiveMetadataReader = directiveMetadataReader; + } + + load(type:Type, location:PrivateComponentLocation) { + var annotation = this.directiveMetadataReader.read(type).annotation; + return this.compiler.compile(type).then((componentProtoView) => { + location.createComponent( + type, annotation, + componentProtoView, + this.eventManager, + this.shadowDomStrategy); + }); + } +} diff --git a/modules/angular2/src/core/compiler/private_component_location.js b/modules/angular2/src/core/compiler/private_component_location.js index 5eb343af09..7a632e0885 100644 --- a/modules/angular2/src/core/compiler/private_component_location.js +++ b/modules/angular2/src/core/compiler/private_component_location.js @@ -24,7 +24,7 @@ export class PrivateComponentLocation { var context = this._elementInjector.createPrivateComponent(type, annotation); var view = componentProtoView.instantiate(this._elementInjector, eventManager); - view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, context); + view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, null, context, null); shadowDomStrategy.attachTemplate(this._elt.domElement, view); diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index f92bc7393a..7731b665d8 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -15,13 +15,15 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {Type, isPresent, BaseException, assertionsEnabled, isJsObject} from 'angular2/src/facade/lang'; import {PromiseWrapper} from 'angular2/src/facade/async'; -import {Injector} from 'angular2/di'; +import {Injector, bind} from 'angular2/di'; import {Lexer, Parser, dynamicChangeDetection, DynamicChangeDetection, Pipe, PipeRegistry} from 'angular2/change_detection'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; +import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; +import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location'; +import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'; import {TemplateLoader} from 'angular2/src/core/compiler/template_loader'; import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; @@ -29,8 +31,9 @@ import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mappe import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver'; import {CssProcessor} from 'angular2/src/core/compiler/css_processor'; +import {EventManager} from 'angular2/src/core/events/event_manager'; -import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations'; +import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations/annotations'; import {Template} from 'angular2/src/core/annotations/template'; import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility'; import {EventEmitter} from 'angular2/src/core/annotations/di'; @@ -41,16 +44,16 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container'; export function main() { describe('integration tests', function() { - var compiler, tplResolver; + var directiveMetadataReader, shadowDomStrategy, compiler, tplResolver; function createCompiler(tplResolver, changedDetection) { var urlResolver = new UrlResolver(); return new Compiler(changedDetection, new TemplateLoader(null, null), - new DirectiveMetadataReader(), + directiveMetadataReader, new Parser(new Lexer()), new CompilerCache(), - new NativeShadowDomStrategy(new StyleUrlResolver(urlResolver)), + shadowDomStrategy, tplResolver, new ComponentUrlMapper(), urlResolver, @@ -60,6 +63,12 @@ export function main() { beforeEach( () => { tplResolver = new MockTemplateResolver(); + + directiveMetadataReader = new DirectiveMetadataReader(); + + var urlResolver = new UrlResolver(); + shadowDomStrategy = new NativeShadowDomStrategy(new StyleUrlResolver(urlResolver)); + compiler = createCompiler(tplResolver, dynamicChangeDetection); }); @@ -68,7 +77,15 @@ export function main() { function createView(pv) { ctx = new MyComp(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, ctx, null); + + view.hydrate(new Injector([ + bind(Compiler).toValue(compiler), + bind(DirectiveMetadataReader).toValue(directiveMetadataReader), + bind(ShadowDomStrategy).toValue(shadowDomStrategy), + bind(EventManager).toValue(null), + PrivateComponentLoader + ]), null, null, ctx, null); + cd = view.changeDetector; } @@ -512,6 +529,25 @@ export function main() { }); })); + it('should support dynamic components', inject([AsyncTestCompleter], (async) => { + tplResolver.setTemplate(MyComp, new Template({ + inline: '', + directives: [DynamicComp] + })); + + compiler.compile(MyComp).then((pv) => { + createView(pv); + + var dynamicComponent = view.locals.get("dynamic"); + expect(dynamicComponent).toBeAnInstanceOf(DynamicComp); + + dynamicComponent.done.then((_) => { + cd.detectChanges(); + expect(view.nodes).toHaveText('hello'); + async.done(); + }); + }); + })); }); if (assertionsEnabled()) { @@ -572,6 +608,30 @@ export function main() { }); } + +@DynamicComponent({ + selector: 'dynamic-comp' +}) +class DynamicComp { + done; + constructor(loader:PrivateComponentLoader, location:PrivateComponentLocation) { + this.done = loader.load(HelloCmp, location); + } +} + +@Component({ + selector: 'hello-cmp' +}) +@Template({ + inline: "{{greeting}}" +}) +class HelloCmp { + greeting:string; + constructor() { + this.greeting = "hello"; + } +} + @Decorator({ selector: '[my-dir]', bind: {'dirProp':'elprop'}