feat: added an ability to dynamically load components

This commit is contained in:
vsavkin 2015-03-13 11:34:20 -07:00
parent 7488456d68
commit 2041860a21
4 changed files with 104 additions and 13 deletions

View File

@ -245,21 +245,18 @@ export class DynamicComponent extends Directive {
bind, bind,
events, events,
services, services,
implementsTypes,
lifecycle lifecycle
}:{ }:{
selector:string, selector:string,
bind:any, bind:Object,
events:any, events:Object,
services:List, services:List,
implementsTypes:List,
lifecycle:List lifecycle:List
}={}) { }={}) {
super({ super({
selector: selector, selector: selector,
bind: bind, bind: bind,
events: events, events: events,
implementsTypes: implementsTypes,
lifecycle: lifecycle lifecycle: lifecycle
}); });

View File

@ -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);
});
}
}

View File

@ -24,7 +24,7 @@ export class PrivateComponentLocation {
var context = this._elementInjector.createPrivateComponent(type, annotation); var context = this._elementInjector.createPrivateComponent(type, annotation);
var view = componentProtoView.instantiate(this._elementInjector, eventManager); 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); shadowDomStrategy.attachTemplate(this._elt.domElement, view);

View File

@ -15,13 +15,15 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
import {Type, isPresent, BaseException, assertionsEnabled, isJsObject} from 'angular2/src/facade/lang'; import {Type, isPresent, BaseException, assertionsEnabled, isJsObject} from 'angular2/src/facade/lang';
import {PromiseWrapper} from 'angular2/src/facade/async'; import {PromiseWrapper} from 'angular2/src/facade/async';
import {Injector} from 'angular2/di'; import {Injector, bind} from 'angular2/di';
import {Lexer, Parser, dynamicChangeDetection, import {Lexer, Parser, dynamicChangeDetection,
DynamicChangeDetection, Pipe, PipeRegistry} from 'angular2/change_detection'; DynamicChangeDetection, Pipe, PipeRegistry} from 'angular2/change_detection';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; 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 {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; 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 {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver'; import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
import {CssProcessor} from 'angular2/src/core/compiler/css_processor'; 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 {Template} from 'angular2/src/core/annotations/template';
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility'; import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {EventEmitter} from 'angular2/src/core/annotations/di'; import {EventEmitter} from 'angular2/src/core/annotations/di';
@ -41,16 +44,16 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container';
export function main() { export function main() {
describe('integration tests', function() { describe('integration tests', function() {
var compiler, tplResolver; var directiveMetadataReader, shadowDomStrategy, compiler, tplResolver;
function createCompiler(tplResolver, changedDetection) { function createCompiler(tplResolver, changedDetection) {
var urlResolver = new UrlResolver(); var urlResolver = new UrlResolver();
return new Compiler(changedDetection, return new Compiler(changedDetection,
new TemplateLoader(null, null), new TemplateLoader(null, null),
new DirectiveMetadataReader(), directiveMetadataReader,
new Parser(new Lexer()), new Parser(new Lexer()),
new CompilerCache(), new CompilerCache(),
new NativeShadowDomStrategy(new StyleUrlResolver(urlResolver)), shadowDomStrategy,
tplResolver, tplResolver,
new ComponentUrlMapper(), new ComponentUrlMapper(),
urlResolver, urlResolver,
@ -60,6 +63,12 @@ export function main() {
beforeEach( () => { beforeEach( () => {
tplResolver = new MockTemplateResolver(); tplResolver = new MockTemplateResolver();
directiveMetadataReader = new DirectiveMetadataReader();
var urlResolver = new UrlResolver();
shadowDomStrategy = new NativeShadowDomStrategy(new StyleUrlResolver(urlResolver));
compiler = createCompiler(tplResolver, dynamicChangeDetection); compiler = createCompiler(tplResolver, dynamicChangeDetection);
}); });
@ -68,7 +77,15 @@ export function main() {
function createView(pv) { function createView(pv) {
ctx = new MyComp(); ctx = new MyComp();
view = pv.instantiate(null, null); 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; cd = view.changeDetector;
} }
@ -512,6 +529,25 @@ export function main() {
}); });
})); }));
it('should support dynamic components', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
inline: '<dynamic-comp #dynamic></dynamic-comp>',
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()) { 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({ @Decorator({
selector: '[my-dir]', selector: '[my-dir]',
bind: {'dirProp':'elprop'} bind: {'dirProp':'elprop'}