refactor(core): remove DynamicComponent

BREAKING CHANGE:
A dynamic component is just a component that has no @View annotation…
This commit is contained in:
Tobias Bosch 2015-04-29 16:52:34 -07:00
parent b71fe311fc
commit 8faf6364dc
17 changed files with 76 additions and 123 deletions

View File

@ -7,7 +7,5 @@ export {
Component as ComponentAnnotation, Component as ComponentAnnotation,
Decorator as DecoratorAnnotation, Decorator as DecoratorAnnotation,
Directive as DirectiveAnnotation, Directive as DirectiveAnnotation,
DynamicComponent as DynamicComponentAnnotation,
Viewport as ViewportAnnotation,
onDestroy, onChange, onAllChangesDone onDestroy, onChange, onAllChangesDone
} from '../annotations_impl/annotations'; } from '../annotations_impl/annotations';

View File

@ -8,7 +8,7 @@ import {DEFAULT} from 'angular2/change_detection';
/** /**
* Directives allow you to attach behavior to elements in the DOM. * Directives allow you to attach behavior to elements in the DOM.
* *
* Directive is an abstract concept, instead use concrete directives: {@link Component}, {@link DynamicComponent}, {@link Decorator}. * Directive is an abstract concept, instead use concrete directives: {@link Component}, or {@link Decorator}.
* *
* A directive consists of a single directive annotation and a controller class. When the directive's `selector` matches * A directive consists of a single directive annotation and a controller class. When the directive's `selector` matches
* elements in the DOM, the following steps occur: * elements in the DOM, the following steps occur:
@ -542,6 +542,51 @@ export class Directive extends Injectable {
* } * }
* ``` * ```
* *
*
* Dynamically loading a component at runtime:
*
* Regular Angular components are statically resolved. Dynamic components allows to resolve a component at runtime
* instead by providing a placeholder into which a regular Angular component can be dynamically loaded. Once loaded,
* the dynamically-loaded component becomes permanent and cannot be changed.
* Dynamic components are declared just like components, but without a `@View` annotation.
*
*
* ## Example
*
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic component
* `DynamicComp` requests loading of the `HelloCmp` component.
*
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be used in other static
* locations.
*
* ```
* @Component({
* selector: 'dynamic-comp'
* })
* class DynamicComp {
* helloCmp:HelloCmp;
* constructor(loader:DynamicComponentLoader, location:ElementRef) {
* loader.load(HelloCmp, location).then((helloCmp) => {
* this.helloCmp = helloCmp;
* });
* }
* }
*
* @Component({
* selector: 'hello-cmp'
* })
* @View({
* template: "{{greeting}}"
* })
* class HelloCmp {
* greeting:string;
* constructor() {
* this.greeting = "hello";
* }
* }
* ```
*
*
* @exportedAs angular2/annotations * @exportedAs angular2/annotations
*/ */
export class Component extends Directive { export class Component extends Directive {
@ -639,90 +684,6 @@ export class Component extends Directive {
} }
} }
/**
* Directive used for dynamically loading components.
*
* Regular Angular components are statically resolved. DynamicComponent allows to you resolve a component at runtime
* instead by providing a placeholder into which a regular Angular component can be dynamically loaded. Once loaded,
* the dynamically-loaded component becomes permanent and cannot be changed.
*
*
* ## Example
*
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic component
* `DynamicComp` requests loading of the `HelloCmp` component.
*
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be used in other static
* locations.
*
* ```
* @DynamicComponent({
* selector: 'dynamic-comp'
* })
* class DynamicComp {
* helloCmp:HelloCmp;
* constructor(loader:DynamicComponentLoader, location:PrivateComponentLocation) {
* loader.load(HelloCmp, location).then((helloCmp) => {
* this.helloCmp = helloCmp;
* });
* }
* }
*
* @Component({
* selector: 'hello-cmp'
* })
* @View({
* template: "{{greeting}}"
* })
* class HelloCmp {
* greeting:string;
* constructor() {
* this.greeting = "hello";
* }
* }
* ```
*
*
*
* @exportedAs angular2/annotations
*/
export class DynamicComponent extends Directive {
/**
* Same as `injectables` in the {@link Component}.
*/
// TODO(vsankin): Please extract into AbstractComponent
injectables:any; //List;
@CONST()
constructor({
selector,
properties,
events,
hostListeners,
hostProperties,
injectables,
lifecycle
}:{
selector:string,
properties:any,
events:List,
hostListeners:any,
hostProperties:any,
injectables:List,
lifecycle:List
}={}) {
super({
selector: selector,
properties: properties,
events: events,
hostListeners: hostListeners,
hostProperties: hostProperties,
lifecycle: lifecycle
});
this.injectables = injectables;
}
}
/** /**
* Directive that attaches behavior to DOM elements. * Directive that attaches behavior to DOM elements.

View File

@ -4,7 +4,7 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
import {DirectiveMetadataReader} from './directive_metadata_reader'; import {DirectiveMetadataReader} from './directive_metadata_reader';
import {Component, DynamicComponent, Decorator} from '../annotations_impl/annotations'; import {Component, Decorator} from '../annotations_impl/annotations';
import {AppProtoView} from './view'; import {AppProtoView} from './view';
import {ProtoViewRef} from './view_ref'; import {ProtoViewRef} from './view_ref';
import {DirectiveBinding} from './element_injector'; import {DirectiveBinding} from './element_injector';
@ -128,8 +128,10 @@ export class Compiler {
// It happens when a template references a component multiple times. // It happens when a template references a component multiple times.
return pvPromise; return pvPromise;
} }
var template = this._templateResolver.resolve(component); var template = this._templateResolver.resolve(component);
if (isBlank(template)) {
return null;
}
if (isPresent(template.renderer)) { if (isPresent(template.renderer)) {
var directives = []; var directives = [];
pvPromise = this._renderer.createImperativeComponentProtoView(template.renderer).then( (renderPv) => { pvPromise = this._renderer.createImperativeComponentProtoView(template.renderer).then( (renderPv) => {
@ -174,9 +176,7 @@ export class Compiler {
}; };
var nestedCall = null; var nestedCall = null;
if (isPresent(nestedComponent)) { if (isPresent(nestedComponent)) {
if (!(nestedComponent.annotation instanceof DynamicComponent)) { nestedCall = this._compile(nestedComponent);
nestedCall = this._compile(nestedComponent);
}
} else if (isPresent(nestedRenderProtoView)) { } else if (isPresent(nestedRenderProtoView)) {
nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false); nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false);
} }
@ -231,7 +231,7 @@ export class Compiler {
var ann = directiveBinding.annotation; var ann = directiveBinding.annotation;
var renderType; var renderType;
var compileChildren = true; var compileChildren = true;
if ((ann instanceof Component) || (ann instanceof DynamicComponent)) { if (ann instanceof Component) {
renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE; renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE;
} else if (ann instanceof Decorator) { } else if (ann instanceof Decorator) {
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE; renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;

View File

@ -4,7 +4,7 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {ChangeDetection, DirectiveIndex} from 'angular2/change_detection'; import {ChangeDetection, DirectiveIndex} from 'angular2/change_detection';
import {Component, DynamicComponent} from '../annotations_impl/annotations'; import {Component} from '../annotations_impl/annotations';
import * as renderApi from 'angular2/src/render/api'; import * as renderApi from 'angular2/src/render/api';
import {AppProtoView} from './view'; import {AppProtoView} from './view';
@ -162,7 +162,7 @@ class SortedDirectives {
this.componentDirective = null; this.componentDirective = null;
ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => { ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => {
var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex]; var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex];
if ((directiveBinding.annotation instanceof Component) || (directiveBinding.annotation instanceof DynamicComponent)) { if (directiveBinding.annotation instanceof Component) {
// component directives need to be the first binding in ElementInjectors! // component directives need to be the first binding in ElementInjectors!
this.componentDirective = directiveBinding; this.componentDirective = directiveBinding;
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder); ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder);

View File

@ -34,7 +34,7 @@ export class TemplateResolver {
return annotation; return annotation;
} }
} }
// No annotation = dynamic component!
throw new BaseException(`No template found for ${stringify(component)}`); return null;
} }
} }

View File

@ -1,8 +1,7 @@
import { import {
ComponentAnnotation, ComponentAnnotation,
DecoratorAnnotation, DecoratorAnnotation
DynamicComponentAnnotation, } from '../annotations/annotations';
ViewportAnnotation} from '../annotations/annotations';
import {ViewAnnotation} from '../annotations/view'; import {ViewAnnotation} from '../annotations/view';
import {AncestorAnnotation, ParentAnnotation} from '../annotations/visibility'; import {AncestorAnnotation, ParentAnnotation} from '../annotations/visibility';
import {AttributeAnnotation, QueryAnnotation} from '../annotations/di'; import {AttributeAnnotation, QueryAnnotation} from '../annotations/di';
@ -25,8 +24,6 @@ function makeDecorator(annotationCls) {
/* from annotations */ /* from annotations */
export var Component = makeDecorator(ComponentAnnotation); export var Component = makeDecorator(ComponentAnnotation);
export var Decorator = makeDecorator(DecoratorAnnotation); export var Decorator = makeDecorator(DecoratorAnnotation);
export var DynamicComponent = makeDecorator(DynamicComponentAnnotation);
export var Viewport = makeDecorator(ViewportAnnotation);
/* from di */ /* from di */
export var Attribute = makeDecorator(AttributeAnnotation); export var Attribute = makeDecorator(AttributeAnnotation);

View File

@ -78,6 +78,10 @@ export class MockTemplateResolver extends TemplateResolver {
if (isBlank(view)) { if (isBlank(view)) {
view = super.resolve(component); view = super.resolve(component);
} }
if (isBlank(view)) {
// dynamic components
return null;
}
var directives = view.directives; var directives = view.directives;
var overrides = MapWrapper.get(this._directiveOverrides, component); var overrides = MapWrapper.get(this._directiveOverrides, component);

View File

@ -87,14 +87,6 @@ export function main() {
}); });
describe('bootstrap factory method', () => { describe('bootstrap factory method', () => {
it('should throw if no View found', inject([AsyncTestCompleter], (async) => {
var refPromise = bootstrap(HelloRootMissingTemplate, testBindings, (e,t) => {throw e;});
PromiseWrapper.then(refPromise, null, (reason) => {
expect(reason.message).toContain('No template found for HelloRootMissingTemplate');
async.done();
});
}));
it('should throw if bootstrapped Directive is not a Component', inject([AsyncTestCompleter], (async) => { it('should throw if bootstrapped Directive is not a Component', inject([AsyncTestCompleter], (async) => {
var refPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e,t) => {throw e;}); var refPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e,t) => {throw e;});
PromiseWrapper.then(refPromise, null, (reason) => { PromiseWrapper.then(refPromise, null, (reason) => {

View File

@ -21,7 +21,7 @@ import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {AppProtoView} from 'angular2/src/core/compiler/view'; import {AppProtoView} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder'; import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Component, DynamicComponent, Decorator} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {Attribute} from 'angular2/src/core/annotations_impl/di'; import {Attribute} from 'angular2/src/core/annotations_impl/di';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {internalProtoView} from 'angular2/src/core/compiler/view_ref'; import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
@ -490,7 +490,7 @@ class NestedComponent {}
class RecursiveComponent {} class RecursiveComponent {}
@DynamicComponent() @Component()
class SomeDynamicComponentDirective {} class SomeDynamicComponentDirective {}
@Decorator() @Decorator()
@ -554,7 +554,8 @@ class FakeTemplateResolver extends TemplateResolver {
resolve(component: Type): View { resolve(component: Type): View {
var template = MapWrapper.get(this._cmpTemplates, component); var template = MapWrapper.get(this._cmpTemplates, component);
if (isBlank(template)) { if (isBlank(template)) {
throw 'No template'; // dynamic component
return null;
} }
return template; return template;
} }

View File

@ -16,7 +16,7 @@ import {
import {TestBed} from 'angular2/src/test_lib/test_bed'; import {TestBed} from 'angular2/src/test_lib/test_bed';
import {Decorator, Component, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Decorator, Component} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader'; import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {ElementRef} from 'angular2/src/core/compiler/element_ref'; import {ElementRef} from 'angular2/src/core/compiler/element_ref';
@ -229,7 +229,7 @@ class ChildComp {
class DynamicallyCreatedComponentService { class DynamicallyCreatedComponentService {
} }
@DynamicComponent({ @Component({
selector: 'dynamic-comp' selector: 'dynamic-comp'
}) })
class DynamicComp { class DynamicComp {

View File

@ -24,7 +24,7 @@ import {Injector, bind} from 'angular2/di';
import {PipeRegistry, defaultPipeRegistry, import {PipeRegistry, defaultPipeRegistry,
ChangeDetection, DynamicChangeDetection, Pipe, ChangeDetectorRef, ON_PUSH} from 'angular2/change_detection'; ChangeDetection, DynamicChangeDetection, Pipe, ChangeDetectorRef, ON_PUSH} from 'angular2/change_detection';
import {Decorator, Component, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Decorator, Component} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility'; import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
import {Attribute} from 'angular2/src/core/annotations_impl/di'; import {Attribute} from 'angular2/src/core/annotations_impl/di';

View File

@ -12,7 +12,7 @@ import {If, For} from 'angular2/directives';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
var testList = null; var testList = null;
@ -109,7 +109,7 @@ class DummyComponent {}
@Decorator({selector: '[dummy-decorator]'}) @Decorator({selector: '[dummy-decorator]'})
class DummyDecorator {} class DummyDecorator {}
@DynamicComponent({selector: 'dynamic-dummy'}) @Component({selector: 'dynamic-dummy'})
class DynamicDummy { class DynamicDummy {
constructor(loader:DynamicComponentLoader, location:ElementRef) { constructor(loader:DynamicComponentLoader, location:ElementRef) {
loader.loadIntoExistingLocation(DummyComponent, location); loader.loadIntoExistingLocation(DummyComponent, location);

View File

@ -2,7 +2,7 @@ import {bootstrap} from 'angular2/angular2';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';

View File

@ -9,7 +9,7 @@ import {document} from 'angular2/src/facade/browser';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
@Component({selector: 'scroll-app'}) @Component({selector: 'scroll-app'})

View File

@ -5,7 +5,7 @@ import {For} from 'angular2/directives';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
export class HasStyle { export class HasStyle {

View File

@ -3,7 +3,7 @@ import {Math} from 'angular2/src/facade/math';
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {Offering, ITEMS, ITEM_HEIGHT, VISIBLE_ITEMS, VIEW_PORT_HEIGHT, import {Offering, ITEMS, ITEM_HEIGHT, VISIBLE_ITEMS, VIEW_PORT_HEIGHT,

View File

@ -5,7 +5,7 @@ import {CompanyNameComponent, OpportunityNameComponent,
// TODO(radokirov): Once the application is transpiled by TS instead of Traceur, // TODO(radokirov): Once the application is transpiled by TS instead of Traceur,
// add those imports back into 'angular2/angular2'; // add those imports back into 'angular2/angular2';
import {Component, Decorator, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations'; import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotations';
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {Offering, ITEM_HEIGHT, COMPANY_NAME_WIDTH, OPPORTUNITY_NAME_WIDTH, import {Offering, ITEM_HEIGHT, COMPANY_NAME_WIDTH, OPPORTUNITY_NAME_WIDTH,