refactor(view_manager): split inPlace views into root and free host views.
BREAKING CHANGE: `AppViewManager.createInPlaceHostView` is replaced by `AppViewManager.createRootHostView` (for bootstrap) and `AppViewManager.createFreeHostView` (for imperative components). The later creates new host elements that are not attached anywhere. To attach them, use `DomRenderer.getHostElement(hostviewRef)` to get the host element. Closes #1920
This commit is contained in:
@@ -11,17 +11,18 @@ import {
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit
|
||||
xit,
|
||||
viewRootNodes
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||
|
||||
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {NgIf} from 'angular2/src/directives/ng_if';
|
||||
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
|
||||
@@ -193,6 +194,37 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('loadAsRoot', () => {
|
||||
|
||||
it('should allow to create, update and destroy components',
|
||||
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector], (tb, async, dcl, doc, injector) => {
|
||||
var rootEl = el('<child-cmp></child-cmp>');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
dcl.loadAsRoot(ChildComp, null, injector).then( (componentRef) => {
|
||||
var view = new ViewProxy(componentRef);
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(rootEl).toHaveText('hello');
|
||||
|
||||
componentRef.instance.ctxProp = 'new';
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(rootEl).toHaveText('new');
|
||||
|
||||
componentRef.dispose();
|
||||
|
||||
expect(rootEl).toHaveText('');
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -200,7 +232,6 @@ export function main() {
|
||||
selector: 'imp-ng-cmp'
|
||||
})
|
||||
@View({
|
||||
renderer: 'imp-ng-cmp-renderer',
|
||||
template: ''
|
||||
})
|
||||
class ImperativeViewComponentUsingNgComponent {
|
||||
@@ -210,7 +241,11 @@ class ImperativeViewComponentUsingNgComponent {
|
||||
var div = el('<div id="impHost"></div>');
|
||||
var shadowViewRef = viewManager.getComponentView(self);
|
||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, '#impHost', null);
|
||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, null).then( (componentRef) => {
|
||||
var element = renderer.getHostElement(componentRef.hostView.render);
|
||||
DOM.appendChild(div, element);
|
||||
return componentRef;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+94
-23
@@ -141,7 +141,7 @@ export function main() {
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||
});
|
||||
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, _c) => {
|
||||
renderer.spy('createRootHostView').andCallFake( (_b, _c) => {
|
||||
var rv = new RenderViewRef();
|
||||
ListWrapper.push(createdRenderViews, rv);
|
||||
return rv;
|
||||
@@ -294,7 +294,7 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('createInPlaceHostView', () => {
|
||||
describe('createFreeHostView', () => {
|
||||
|
||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
@@ -314,27 +314,26 @@ export function main() {
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(
|
||||
internalView(manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null))
|
||||
internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null))
|
||||
).toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
});
|
||||
|
||||
it('should attachAndHydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), injector);
|
||||
expect(utils.spy('attachAndHydrateInPlaceHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), injector);
|
||||
expect(utils.spy('attachAndHydrateFreeHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
var elementOrSelector = 'someSelector';
|
||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), elementOrSelector, wrapPv(hostProtoView), null)
|
||||
expect(renderer.spy('createInPlaceHostView')).toHaveBeenCalledWith(parentView.render, elementOrSelector, hostProtoView.render);
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(hostProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null);
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
@@ -343,7 +342,7 @@ export function main() {
|
||||
});
|
||||
|
||||
|
||||
describe('destroyInPlaceHostView', () => {
|
||||
describe('destroyFreeHostView', () => {
|
||||
describe('basic functionality', () => {
|
||||
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
||||
beforeEach( () => {
|
||||
@@ -355,29 +354,29 @@ export function main() {
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
hostView = internalView(manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null));
|
||||
hostView = internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('detachInPlaceHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should destroy and clear the render view', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('destroyInPlaceHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||
it('should detach the render view', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -387,6 +386,78 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('createRootHostView', () => {
|
||||
|
||||
var hostProtoView;
|
||||
beforeEach( () => {
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(
|
||||
internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null))
|
||||
).toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, injector);
|
||||
expect(utils.spy('hydrateRootHostView')).toHaveBeenCalledWith(createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view using the component selector', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null)
|
||||
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, 'someComponent');
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should allow to override the selector', () => {
|
||||
var selector = 'someOtherSelector';
|
||||
manager.createRootHostView(wrapPv(hostProtoView), selector, null)
|
||||
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, selector);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyRootHostView', () => {
|
||||
var hostProtoView, hostView, hostRenderViewRef;
|
||||
beforeEach( () => {
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should destroy the render view', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
@@ -483,19 +554,19 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var directiveResolver;
|
||||
@@ -170,7 +169,7 @@ export function main() {
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
@@ -200,7 +199,7 @@ export function main() {
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyActionAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyActionAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
@@ -268,6 +267,27 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('hydrateRootHostView', () => {
|
||||
var hostView;
|
||||
|
||||
function createViews() {
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder()
|
||||
]);
|
||||
hostView = createView(hostPv);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the given injector and an empty host element injector", () => {
|
||||
var injector = createInjector();
|
||||
createViews();
|
||||
|
||||
utils.hydrateRootHostView(hostView, injector);
|
||||
expect(hostView.rootElementInjectors[0].spy('instantiateDirectives'))
|
||||
.toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user