Skip to content

Commit 5dee8e2

Browse files
committed
fix(views): remove dynamic component views, free host views, free embedded views
Closes angular#2472 Closes angular#2339 BREAKING CHANGE - `Compiler.compile` has been removed, the only way to compile components dynamically is via `Compiler.compileInHost` - `DynamicComponentLoader.loadIntoExistingLocation` has changed: * renamed into `loadIntoLocation` * will always create the host element as well * requires an element with a variable inside of the host component view next to which it will load new component. - `DynamicComponentLoader.loadNextToExistingLocation` was renamed into `DynamicComponentLoader.loadNextToLocation` - `DynamicComponentLoader.loadIntoNewLocation` is removed * use `DynamicComponentLoader.loadNextToLocation` instead and then move the view nodes manually around via `DomRenderer.getRootNodes()` - `AppViewManager.{create,destroy}Free{Host,Embedded}View` was removed * use `AppViewManager.createViewInContainer` and then move the view nodes manually around via `DomRenderer.getRootNodes()` - `Renderer.detachFreeView` was removed. Use `DomRenderer.getRootNodes()` to get the root nodes of a view and detach them manually.
1 parent df6aced commit 5dee8e2

28 files changed

+389
-1048
lines changed

modules/angular2/src/core/annotations_impl/annotations.ts

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -848,54 +848,6 @@ export interface ComponentArgs {
848848
* ```
849849
*
850850
*
851-
* Dynamically loading a component at runtime:
852-
*
853-
* Regular Angular components are statically resolved. Dynamic components allows to resolve a
854-
* component at runtime
855-
* instead by providing a placeholder into which a regular Angular component can be dynamically
856-
* loaded. Once loaded,
857-
* the dynamically-loaded component becomes permanent and cannot be changed.
858-
* Dynamic components are declared just like components, but without a `@View` annotation.
859-
*
860-
*
861-
* ## Example
862-
*
863-
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic
864-
* component
865-
* `DynamicComp` requests loading of the `HelloCmp` component.
866-
*
867-
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be
868-
* used in other static
869-
* locations.
870-
*
871-
* ```
872-
* @Component({
873-
* selector: 'dynamic-comp'
874-
* })
875-
* class DynamicComp {
876-
* helloCmp:HelloCmp;
877-
* constructor(loader:DynamicComponentLoader, location:ElementRef) {
878-
* loader.load(HelloCmp, location).then((helloCmp) => {
879-
* this.helloCmp = helloCmp;
880-
* });
881-
* }
882-
* }
883-
*
884-
* @Component({
885-
* selector: 'hello-cmp'
886-
* })
887-
* @View({
888-
* template: "{{greeting}}"
889-
* })
890-
* class HelloCmp {
891-
* greeting:string;
892-
* constructor() {
893-
* this.greeting = "hello";
894-
* }
895-
* }
896-
* ```
897-
*
898-
*
899851
* @exportedAs angular2/annotations
900852
*/
901853
@CONST()

modules/angular2/src/core/compiler/compiler.ts

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import * as renderApi from 'angular2/src/render/api';
3333
@Injectable()
3434
export class CompilerCache {
3535
_cache: Map<Type, AppProtoView> = MapWrapper.create();
36+
_hostCache: Map<Type, AppProtoView> = MapWrapper.create();
3637

3738
set(component: Type, protoView: AppProtoView): void {
3839
MapWrapper.set(this._cache, component, protoView);
@@ -43,7 +44,19 @@ export class CompilerCache {
4344
return normalizeBlank(result);
4445
}
4546

46-
clear(): void { MapWrapper.clear(this._cache); }
47+
setHost(component: Type, protoView: AppProtoView): void {
48+
MapWrapper.set(this._hostCache, component, protoView);
49+
}
50+
51+
getHost(component: Type): AppProtoView {
52+
var result = MapWrapper.get(this._hostCache, component);
53+
return normalizeBlank(result);
54+
}
55+
56+
clear(): void {
57+
MapWrapper.clear(this._cache);
58+
MapWrapper.clear(this._hostCache);
59+
}
4760
}
4861

4962
/**
@@ -94,20 +107,19 @@ export class Compiler {
94107
Compiler._assertTypeIsComponent(componentBinding);
95108

96109
var directiveMetadata = componentBinding.metadata;
97-
return this._render.compileHost(directiveMetadata)
98-
.then((hostRenderPv) => {
99-
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
100-
})
101-
.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
102-
}
103-
104-
compile(component: Type): Promise<ProtoViewRef> {
105-
var componentBinding = this._bindDirective(component);
106-
Compiler._assertTypeIsComponent(componentBinding);
107-
var pvOrPromise = this._compile(componentBinding);
108-
var pvPromise = isPromise(pvOrPromise) ? <Promise<AppProtoView>>pvOrPromise :
109-
PromiseWrapper.resolve(pvOrPromise);
110-
return pvPromise.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
110+
var hostPvPromise;
111+
var component = <Type>componentBinding.key.token;
112+
var hostAppProtoView = this._compilerCache.getHost(component);
113+
if (isPresent(hostAppProtoView)) {
114+
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
115+
} else {
116+
hostPvPromise = this._render.compileHost(directiveMetadata)
117+
.then((hostRenderPv) => {
118+
return this._compileNestedProtoViews(componentBinding, hostRenderPv,
119+
[componentBinding]);
120+
});
121+
}
122+
return hostPvPromise.then((hostAppProtoView) => { return new ProtoViewRef(hostAppProtoView); });
111123
}
112124

113125
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
@@ -128,9 +140,6 @@ export class Compiler {
128140
return pvPromise;
129141
}
130142
var template = this._templateResolver.resolve(component);
131-
if (isBlank(template)) {
132-
return null;
133-
}
134143

135144
var directives = this._flattenDirectives(template);
136145

@@ -160,16 +169,17 @@ export class Compiler {
160169
var protoViews =
161170
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
162171
var protoView = protoViews[0];
163-
// TODO(tbosch): we should be caching host protoViews as well!
164-
// -> need a separate cache for this...
165-
if (renderPv.type === renderApi.ViewType.COMPONENT && isPresent(componentBinding)) {
166-
// Populate the cache before compiling the nested components,
167-
// so that components can reference themselves in their template.
172+
if (isPresent(componentBinding)) {
168173
var component = componentBinding.key.token;
169-
this._compilerCache.set(component, protoView);
170-
MapWrapper.delete(this._compiling, component);
174+
if (renderPv.type === renderApi.ViewType.COMPONENT) {
175+
// Populate the cache before compiling the nested components,
176+
// so that components can reference themselves in their template.
177+
this._compilerCache.set(component, protoView);
178+
MapWrapper.delete(this._compiling, component);
179+
} else {
180+
this._compilerCache.setHost(component, protoView);
181+
}
171182
}
172-
173183
var nestedPVPromises = [];
174184
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
175185
var nestedComponent = elementBinder.componentDirective;
@@ -179,7 +189,7 @@ export class Compiler {
179189
if (isPromise(nestedCall)) {
180190
ListWrapper.push(nestedPVPromises,
181191
(<Promise<AppProtoView>>nestedCall).then(elementBinderDone));
182-
} else if (isPresent(nestedCall)) {
192+
} else {
183193
elementBinderDone(<AppProtoView>nestedCall);
184194
}
185195
});

modules/angular2/src/core/compiler/dynamic_component_loader.ts

Lines changed: 25 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,14 @@ export class ComponentRef {
2525
export class DynamicComponentLoader {
2626
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) {}
2727

28-
/**
29-
* Loads a component into the location given by the provided ElementRef. The loaded component
30-
* receives injection as if it in the place of the provided ElementRef.
31-
*/
32-
loadIntoExistingLocation(typeOrBinding, location: ElementRef,
33-
injector: Injector = null): Promise<ComponentRef> {
34-
var binding = this._getBinding(typeOrBinding);
35-
return this._compiler.compile(binding.token)
36-
.then(componentProtoViewRef => {
37-
this._viewManager.createDynamicComponentView(location, componentProtoViewRef, binding,
38-
injector);
39-
var component = this._viewManager.getComponent(location);
40-
var dispose = () => { this._viewManager.destroyDynamicComponent(location); };
41-
return new ComponentRef(location, component, dispose);
42-
});
43-
}
44-
4528
/**
4629
* Loads a root component that is placed at the first element that matches the
4730
* component's selector.
4831
* The loaded component receives injection normally as a hosted view.
4932
*/
50-
loadAsRoot(typeOrBinding, overrideSelector: string = null,
33+
loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string = null,
5134
injector: Injector = null): Promise<ComponentRef> {
52-
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
35+
return this._compiler.compileInHost(typeOrBinding)
5336
.then(hostProtoViewRef => {
5437
var hostViewRef =
5538
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
@@ -62,55 +45,37 @@ export class DynamicComponentLoader {
6245
}
6346

6447
/**
65-
* Loads a component into a free host view that is not yet attached to
66-
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
67-
* The loaded component receives injection normally as a hosted view.
48+
* Loads a component into the component view of the provided ElementRef
49+
* next to the element with the given name
50+
* The loaded component receives
51+
* injection normally as a hosted view.
6852
*/
69-
loadIntoNewLocation(typeOrBinding, parentComponentLocation: ElementRef,
70-
injector: Injector = null): Promise<ComponentRef> {
71-
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
53+
loadIntoLocation(typeOrBinding: Type | Binding, hostLocation: ElementRef, anchorName: string,
54+
injector: Injector = null): Promise<ComponentRef> {
55+
return this.loadNextToLocation(
56+
typeOrBinding, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
57+
injector);
58+
}
59+
60+
/**
61+
* Loads a component next to the provided ElementRef. The loaded component receives
62+
* injection normally as a hosted view.
63+
*/
64+
loadNextToLocation(typeOrBinding: Type | Binding, location: ElementRef,
65+
injector: Injector = null): Promise<ComponentRef> {
66+
return this._compiler.compileInHost(typeOrBinding)
7267
.then(hostProtoViewRef => {
73-
var hostViewRef = this._viewManager.createFreeHostView(parentComponentLocation,
74-
hostProtoViewRef, injector);
68+
var viewContainer = this._viewManager.getViewContainer(location);
69+
var hostViewRef =
70+
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
7571
var newLocation = new ElementRef(hostViewRef, 0);
7672
var component = this._viewManager.getComponent(newLocation);
7773

7874
var dispose = () => {
79-
this._viewManager.destroyFreeHostView(parentComponentLocation, hostViewRef);
75+
var index = viewContainer.indexOf(hostViewRef);
76+
viewContainer.remove(index);
8077
};
8178
return new ComponentRef(newLocation, component, dispose);
8279
});
8380
}
84-
85-
/**
86-
* Loads a component next to the provided ElementRef. The loaded component receives
87-
* injection normally as a hosted view.
88-
*/
89-
loadNextToExistingLocation(typeOrBinding, location: ElementRef,
90-
injector: Injector = null): Promise<ComponentRef> {
91-
var binding = this._getBinding(typeOrBinding);
92-
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
93-
var viewContainer = this._viewManager.getViewContainer(location);
94-
var hostViewRef =
95-
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
96-
var newLocation = new ElementRef(hostViewRef, 0);
97-
var component = this._viewManager.getComponent(newLocation);
98-
99-
var dispose = () => {
100-
var index = viewContainer.indexOf(hostViewRef);
101-
viewContainer.remove(index);
102-
};
103-
return new ComponentRef(newLocation, component, dispose);
104-
});
105-
}
106-
107-
private _getBinding(typeOrBinding): Binding {
108-
var binding;
109-
if (typeOrBinding instanceof Binding) {
110-
binding = typeOrBinding;
111-
} else {
112-
binding = bind(typeOrBinding).toClass(typeOrBinding);
113-
}
114-
return binding;
115-
}
11681
}

modules/angular2/src/core/compiler/element_binder.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ export class ElementBinder {
2424
return isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
2525
}
2626

27-
hasDynamicComponent() {
28-
return isPresent(this.componentDirective) && isBlank(this.nestedProtoView);
29-
}
30-
3127
hasEmbeddedProtoView() {
3228
return !isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
3329
}

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -662,9 +662,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
662662
private _preBuiltObjects = null;
663663
private _constructionCounter: number = 0;
664664

665-
private _dynamicallyCreatedComponent: any;
666-
private _dynamicallyCreatedComponentBinding: DirectiveBinding;
667-
668665
// Queries are added during construction or linking with a new parent.
669666
// They are never removed.
670667
private _query0: QueryRef;
@@ -693,20 +690,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
693690
this._lightDomAppInjector = null;
694691
this._shadowDomAppInjector = null;
695692
this._strategy.callOnDestroy();
696-
this.destroyDynamicComponent();
697693
this._strategy.clearInstances();
698694
this._constructionCounter = 0;
699695
}
700696

701-
destroyDynamicComponent(): void {
702-
if (isPresent(this._dynamicallyCreatedComponentBinding) &&
703-
this._dynamicallyCreatedComponentBinding.callOnDestroy) {
704-
this._dynamicallyCreatedComponent.onDestroy();
705-
this._dynamicallyCreatedComponentBinding = null;
706-
this._dynamicallyCreatedComponent = null;
707-
}
708-
}
709-
710697
onAllChangesDone(): void {
711698
if (isPresent(this._query0) && this._query0.originator === this)
712699
this._query0.list.fireCallbacks();
@@ -743,14 +730,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
743730
}
744731
}
745732

746-
dynamicallyCreateComponent(componentDirective: DirectiveBinding, parentInjector: Injector): any {
747-
this._shadowDomAppInjector =
748-
this._createShadowDomAppInjector(componentDirective, parentInjector);
749-
this._dynamicallyCreatedComponentBinding = componentDirective;
750-
this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding);
751-
return this._dynamicallyCreatedComponent;
752-
}
753-
754733
private _checkShadowDomAppInjector(shadowDomAppInjector: Injector): void {
755734
if (this._proto._firstBindingIsComponent && isBlank(shadowDomAppInjector)) {
756735
throw new BaseException(
@@ -761,18 +740,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
761740
}
762741
}
763742

764-
get(token): any {
765-
if (this._isDynamicallyLoadedComponent(token)) {
766-
return this._dynamicallyCreatedComponent;
767-
}
768-
769-
return this._getByKey(Key.get(token), self, false, null);
770-
}
771-
772-
private _isDynamicallyLoadedComponent(token): boolean {
773-
return isPresent(this._dynamicallyCreatedComponentBinding) &&
774-
Key.get(token) === this._dynamicallyCreatedComponentBinding.key;
775-
}
743+
get(token): any { return this._getByKey(Key.get(token), self, false, null); }
776744

777745
hasDirective(type: Type): boolean {
778746
return this._strategy.getObjByKeyId(Key.get(type).id, LIGHT_DOM_AND_SHADOW_DOM) !== _undefined;
@@ -796,17 +764,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
796764
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef());
797765
}
798766

799-
getDynamicallyLoadedComponent(): any { return this._dynamicallyCreatedComponent; }
800-
801767
directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; }
802768

803769
private _isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); }
804770

805-
private _isDynamicallyLoadedComponentKey(key: Key): boolean {
806-
return isPresent(this._dynamicallyCreatedComponentBinding) &&
807-
key.id === this._dynamicallyCreatedComponentBinding.key.id;
808-
}
809-
810771
_new(binding: ResolvedBinding): any {
811772
if (this._constructionCounter++ > this._strategy.getMaxDirectives()) {
812773
throw new CyclicDependencyError(binding.key);
@@ -1113,8 +1074,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
11131074

11141075
if (isPresent(this._host) && this._host._isComponentKey(key)) {
11151076
return this._host.getComponent();
1116-
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
1117-
return this._host.getDynamicallyLoadedComponent();
11181077
} else if (optional) {
11191078
return this._appInjector(requestor).getOptional(key);
11201079
} else {
@@ -1123,8 +1082,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
11231082
}
11241083

11251084
private _appInjector(requestor: Key): Injector {
1126-
if (isPresent(requestor) &&
1127-
(this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
1085+
if (isPresent(requestor) && this._isComponentKey(requestor)) {
11281086
return this._shadowDomAppInjector;
11291087
} else {
11301088
return this._lightDomAppInjector;

0 commit comments

Comments
 (0)