feat(compiler): added the DynamicComponent annotation

This commit is contained in:
vsavkin 2015-03-13 11:33:57 -07:00
parent 1872b03fb8
commit b69f3043e0
8 changed files with 90 additions and 11 deletions

View File

@ -233,6 +233,40 @@ export class Component extends Directive {
} }
} }
/**
* @publicModule angular2/angular2
*/
export class DynamicComponent extends Directive {
services:any; //List;
@CONST()
constructor({
selector,
bind,
events,
services,
implementsTypes,
lifecycle
}:{
selector:string,
bind:any,
events:any,
services:List,
implementsTypes:List,
lifecycle:List
}={}) {
super({
selector: selector,
bind: bind,
events: events,
implementsTypes: implementsTypes,
lifecycle: lifecycle
});
this.services = services;
}
}
/** /**
* @publicModule angular2/angular2 * @publicModule angular2/angular2
*/ */

View File

@ -163,7 +163,7 @@ export class Compiler {
var nestedPVPromises = []; var nestedPVPromises = [];
for (var i = 0; i < compileElements.length; i++) { for (var i = 0; i < compileElements.length; i++) {
var ce = compileElements[i]; var ce = compileElements[i];
if (isPresent(ce.componentDirective)) { if (ce.hasNestedView) {
this._compileNestedProtoView(ce, nestedPVPromises); this._compileNestedProtoView(ce, nestedPVPromises);
} }
} }

View File

@ -2,7 +2,7 @@ import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection
import {DOM} from 'angular2/src/dom/dom_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter';
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang'; import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
import {DirectiveMetadata} from '../directive_metadata'; import {DirectiveMetadata} from '../directive_metadata';
import {Decorator, Component, Viewport} from '../../annotations/annotations'; import {Decorator, Component, Viewport, DynamicComponent} from '../../annotations/annotations';
import {ElementBinder} from '../element_binder'; import {ElementBinder} from '../element_binder';
import {ProtoElementInjector} from '../element_injector'; import {ProtoElementInjector} from '../element_injector';
import {ProtoView} from '../view'; import {ProtoView} from '../view';
@ -30,6 +30,7 @@ export class CompileElement {
decoratorDirectives:List<DirectiveMetadata>; decoratorDirectives:List<DirectiveMetadata>;
viewportDirective:DirectiveMetadata; viewportDirective:DirectiveMetadata;
componentDirective:DirectiveMetadata; componentDirective:DirectiveMetadata;
hasNestedView:boolean;
_allDirectives:List<DirectiveMetadata>; _allDirectives:List<DirectiveMetadata>;
isViewRoot:boolean; isViewRoot:boolean;
hasBindings:boolean; hasBindings:boolean;
@ -54,6 +55,7 @@ export class CompileElement {
this.decoratorDirectives = null; this.decoratorDirectives = null;
this.viewportDirective = null; this.viewportDirective = null;
this.componentDirective = null; this.componentDirective = null;
this.hasNestedView = false;
this._allDirectives = null; this._allDirectives = null;
this.isViewRoot = false; this.isViewRoot = false;
this.hasBindings = false; this.hasBindings = false;
@ -157,6 +159,9 @@ export class CompileElement {
this.viewportDirective = directive; this.viewportDirective = directive;
} else if (annotation instanceof Component) { } else if (annotation instanceof Component) {
this.componentDirective = directive; this.componentDirective = directive;
this.hasNestedView = true;
} else if (annotation instanceof DynamicComponent) {
this.componentDirective = directive;
} }
} }

View File

@ -5,7 +5,7 @@ import {SelectorMatcher} from '../selector';
import {CssSelector} from '../selector'; import {CssSelector} from '../selector';
import {DirectiveMetadata} from '../directive_metadata'; import {DirectiveMetadata} from '../directive_metadata';
import {Component, Viewport} from '../../annotations/annotations'; import {DynamicComponent, Component, Viewport} from '../../annotations/annotations';
import {CompileStep} from './compile_step'; import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element'; import {CompileElement} from './compile_element';
import {CompileControl} from './compile_control'; import {CompileControl} from './compile_control';
@ -109,6 +109,9 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
// check if the directive is compatible with the current element // check if the directive is compatible with the current element
function checkDirectiveValidity(directive, current, isTemplateElement) { function checkDirectiveValidity(directive, current, isTemplateElement) {
var isComponent = directive.annotation instanceof Component || directive.annotation instanceof DynamicComponent;
var alreadyHasComponent = isPresent(current.componentDirective);
if (directive.annotation instanceof Viewport) { if (directive.annotation instanceof Viewport) {
if (!isTemplateElement) { if (!isTemplateElement) {
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` + throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
@ -118,7 +121,8 @@ function checkDirectiveValidity(directive, current, isTemplateElement) {
} }
} else if (isTemplateElement) { } else if (isTemplateElement) {
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`); throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
} else if ((directive.annotation instanceof Component) && isPresent(current.componentDirective)) {
} else if (isComponent && alreadyHasComponent) {
throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`); throw new BaseException(`Multiple component directives not allowed on the same element - check ${current.elementDescription}`);
} }
} }

View File

@ -171,7 +171,7 @@ export class View {
} }
} }
if (isPresent(componentDirective)) { if (isPresent(binders[i].nestedProtoView) && isPresent(componentDirective)) {
this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector, this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector,
elementInjector, this.lightDoms[i], elementInjector.getComponent(), null); elementInjector, this.lightDoms[i], elementInjector.getComponent(), null);
} }
@ -441,7 +441,7 @@ export class ProtoView {
// componentChildViews // componentChildViews
var lightDom = null; var lightDom = null;
var bindingPropagationConfig = null; var bindingPropagationConfig = null;
if (isPresent(binder.componentDirective)) { if (isPresent(binder.nestedProtoView) && isPresent(binder.componentDirective)) {
var strategy = this.shadowDomStrategy; var strategy = this.shadowDomStrategy;
var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager); var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager);
changeDetector.addChild(childView.changeDetector); changeDetector.addChild(childView.changeDetector);

View File

@ -114,6 +114,7 @@ export function runCompilerCommonTests() {
var compiler = createCompiler( (parent, current, control) => { var compiler = createCompiler( (parent, current, control) => {
if (DOM.hasClass(current.element, 'nested')) { if (DOM.hasClass(current.element, 'nested')) {
current.componentDirective = reader.read(NestedComponent); current.componentDirective = reader.read(NestedComponent);
current.hasNestedView = true;
current.inheritedProtoView = parent.inheritedProtoView; current.inheritedProtoView = parent.inheritedProtoView;
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null); current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null);
} else { } else {
@ -163,6 +164,7 @@ export function runCompilerCommonTests() {
it('should allow recursive components', inject([AsyncTestCompleter], (async) => { it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler( (parent, current, control) => { var compiler = createCompiler( (parent, current, control) => {
current.hasNestedView = true;
current.inheritedProtoView = new ProtoView(current.element, null, null); current.inheritedProtoView = new ProtoView(current.element, null, null);
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null); current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null);
current.componentDirective = reader.read(RecursiveComponent); current.componentDirective = reader.read(RecursiveComponent);
@ -191,6 +193,7 @@ export function runCompilerCommonTests() {
var compiler = createCompiler((parent, current, control) => { var compiler = createCompiler((parent, current, control) => {
if (DOM.hasClass(current.element, 'parent')) { if (DOM.hasClass(current.element, 'parent')) {
current.hasNestedView = true;
current.componentDirective = reader.read(NestedComponent); current.componentDirective = reader.read(NestedComponent);
current.inheritedProtoView = parent.inheritedProtoView; current.inheritedProtoView = parent.inheritedProtoView;
current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null); current.inheritedElementBinder = current.inheritedProtoView.bindElement(null, 0, null);

View File

@ -1,4 +1,4 @@
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib'; import {describe, beforeEach, it, xit, expect, iit, ddescribe, el} from 'angular2/test_lib';
import {isPresent, assertionsEnabled} from 'angular2/src/facade/lang'; import {isPresent, assertionsEnabled} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {DirectiveParser} from 'angular2/src/core/compiler/pipeline/directive_parser'; import {DirectiveParser} from 'angular2/src/core/compiler/pipeline/directive_parser';
@ -6,7 +6,7 @@ import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipel
import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step'; import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step';
import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element'; import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element';
import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control'; import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control';
import {Component, Decorator, Viewport} from 'angular2/src/core/annotations/annotations'; import {Component, DynamicComponent, Decorator, Viewport} from 'angular2/src/core/annotations/annotations';
import {Template} from 'angular2/src/core/annotations/template'; import {Template} from 'angular2/src/core/annotations/template';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Lexer, Parser} from 'angular2/change_detection'; import {Lexer, Parser} from 'angular2/change_detection';
@ -24,7 +24,9 @@ export function main() {
SomeViewport, SomeViewport,
SomeViewport2, SomeViewport2,
SomeComponent, SomeComponent,
SomeComponent2 SomeComponent2,
SomeDynamicComponent,
SomeDynamicComponent2
]; ];
}); });
@ -105,6 +107,29 @@ export function main() {
}); });
}); });
describe("dynamic component directives", () => {
it('should detect dynamic component', () => {
var results = createPipeline().process(el('<div some-dynamic-comp></div>'));
expect(results[0].componentDirective).toEqual(reader.read(SomeDynamicComponent));
});
it('should not allow multiple dynamic component directives on the same element', () => {
expect( () => {
createPipeline().process(
el('<div some-dynamic-comp some-dynamic-comp2></div>')
);
}).toThrowError(new RegExp('Multiple component directives not allowed on the same element'));
});
it('should not allow component and dynamic directives on the same element', () => {
expect( () => {
createPipeline().process(
el('<div some-dynamic-comp some-comp></div>')
);
}).toThrowError(new RegExp('Multiple component directives not allowed on the same element'));
});
});
describe('viewport directives', () => { describe('viewport directives', () => {
it('should detect them in attributes', () => { it('should detect them in attributes', () => {
var results = createPipeline().process(el('<template some-templ></template>')); var results = createPipeline().process(el('<template some-templ></template>'));
@ -236,8 +261,17 @@ class SomeComponent {}
@Component({selector: '[some-comp2]'}) @Component({selector: '[some-comp2]'})
class SomeComponent2 {} class SomeComponent2 {}
@DynamicComponent({selector: '[some-dynamic-comp]'})
class SomeDynamicComponent {}
@DynamicComponent({selector: '[some-dynamic-comp2]'})
class SomeDynamicComponent2 {}
@Component() @Component()
@Template({ @Template({
directives: [SomeDecorator, SomeViewport, SomeViewport2, SomeComponent, SomeComponent2] directives: [SomeDecorator, SomeViewport, SomeViewport2,
SomeComponent, SomeComponent2,
SomeDynamicComponent, SomeDynamicComponent2
]
}) })
class MyComp {} class MyComp {}

View File

@ -101,7 +101,6 @@ export function main() {
}).process(el('<div></div>')); }).process(el('<div></div>'));
assertBinding(results[0], true); assertBinding(results[0], true);
}); });
}); });
} }