refactor(render): remove recursion from renderer

The goal is to make implementing a renderer straight forward.

BREAKING_CHANGE:

- Renderer interface was redone / simplified.
- `DirectDomRenderer` was replaced by `DomRenderer`.
- `DirectDomRenderer.setImperativeComponentRootNodes` is replaced
  by the following 2 steps:
    1. `ViewManager.getComponentView(elementRef) -> ViewRef`
    2. `DomRenderer.setComponentViewRootNodes(viewRef, rootNodes)`
- all `@View` annotations need to have a template, but the template
  may be empty. Previously views that had a `renderer` property did
  not have to have a `template`.
- `dynamicComponentLoader.loadIntoNewLocation` does no more allow
  to pass an element, but requires a css selector.
  Special syntax: `:document` can be used as prefix to search globally
  on the document instead of in the provided parent view.

Part of #1675
This commit is contained in:
Tobias Bosch
2015-05-06 10:49:42 -07:00
parent d2507ac760
commit c68fa27444
51 changed files with 1242 additions and 2228 deletions
-19
View File
@@ -391,25 +391,6 @@ export function main() {
});
}));
it('should create imperative proto views', inject([AsyncTestCompleter], (async) => {
renderCompiler.spy('createImperativeComponentProtoView').andCallFake( (rendererId) => {
return PromiseWrapper.resolve(
createRenderProtoView([])
);
});
tplResolver.setView(MainComponent, new View({renderer: 'some-renderer'}));
var mainProtoView = createProtoView();
var compiler = createCompiler(
[],
[mainProtoView]
);
compiler.compile(MainComponent).then( (protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
expect(renderCompiler.spy('createImperativeComponentProtoView')).toHaveBeenCalledWith('some-renderer');
async.done();
});
}));
it('should throw for non component types', () => {
var compiler = createCompiler([], []);
expect(
@@ -21,9 +21,9 @@ 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 {If} from 'angular2/src/directives/if';
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
export function main() {
describe('DynamicComponentLoader', function () {
@@ -200,15 +200,17 @@ export function main() {
selector: 'imp-ng-cmp'
})
@View({
renderer: 'imp-ng-cmp-renderer'
renderer: 'imp-ng-cmp-renderer',
template: ''
})
class ImperativeViewComponentUsingNgComponent {
done;
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, renderer:DirectDomRenderer) {
var div = el('<div></div>');
renderer.setImperativeComponentRootNodes(self.parentView.render, self.boundElementIndex, [div]);
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, div, null);
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, viewManager:AppViewManager, renderer:DomRenderer) {
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);
}
}
+7 -4
View File
@@ -37,7 +37,8 @@ import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
import {Compiler} from 'angular2/src/core/compiler/compiler';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
export function main() {
describe('integration tests', function() {
@@ -863,13 +864,15 @@ export function main() {
selector: 'simple-imp-cmp'
})
@View({
renderer: 'simple-imp-cmp-renderer'
renderer: 'simple-imp-cmp-renderer',
template: ''
})
class SimpleImperativeViewComponent {
done;
constructor(self:ElementRef, renderer:DirectDomRenderer) {
renderer.setImperativeComponentRootNodes(self.parentView.render, self.boundElementIndex, [el('hello imp view')]);
constructor(self:ElementRef, viewManager:AppViewManager, renderer:DomRenderer) {
var shadowViewRef = viewManager.getComponentView(self);
renderer.setComponentViewRootNodes(shadowViewRef.render, [el('hello imp view')]);
}
}
+33 -46
View File
@@ -94,11 +94,15 @@ export function main() {
}, {});
}
function createView(pv=null) {
function createView(pv=null, renderViewRef=null) {
if (isBlank(pv)) {
pv = createProtoView();
}
if (isBlank(renderViewRef)) {
renderViewRef = new RenderViewRef();
}
var view = new AppView(renderer, pv, MapWrapper.create());
view.render = renderViewRef;
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
for (var i=0; i<pv.elementBinders.length; i++) {
elementInjectors[i] = createElementInjector();
@@ -121,8 +125,8 @@ export function main() {
createdViews = [];
createdRenderViews = [];
utils.spy('createView').andCallFake( (proto, _a, _b) => {
var view = createView(proto);
utils.spy('createView').andCallFake( (proto, renderViewRef, _a, _b) => {
var view = createView(proto, renderViewRef);
ListWrapper.push(createdViews, view);
return view;
});
@@ -137,23 +141,15 @@ export function main() {
}
ListWrapper.insert(viewContainer.views, atIndex, childView);
});
var createRenderViewRefs = function(renderPvRef) {
var res = [];
for (var i=0; i<renderPvRef.nestedComponentCount+1; i++) {
var renderViewRef = new RenderViewRef();
ListWrapper.push(res, renderViewRef);
ListWrapper.push(createdRenderViews, renderViewRef);
}
return res;
}
renderer.spy('createDynamicComponentView').andCallFake( (_a, _b, childPvRef) => {
return createRenderViewRefs(childPvRef);
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, _c) => {
var rv = new RenderViewRef();
ListWrapper.push(createdRenderViews, rv);
return rv;
});
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, childPvRef) => {
return createRenderViewRefs(childPvRef);
});
renderer.spy('createViewInContainer').andCallFake( (_a, _b, childPvRef) => {
return createRenderViewRefs(childPvRef);
renderer.spy('createView').andCallFake( (_a) => {
var rv = new RenderViewRef();
ListWrapper.push(createdRenderViews, rv);
return rv;
});
});
@@ -165,7 +161,6 @@ export function main() {
hostView = createView(createProtoView(
[createComponentElBinder(null)]
));
hostView.render = new RenderViewRef();
componentProtoView = createProtoView();
});
@@ -186,11 +181,13 @@ export function main() {
internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))
).toBe(createdView);
expect(utils.spy('createView')).not.toHaveBeenCalled();
expect(renderer.spy('createView')).not.toHaveBeenCalled();
});
it('should attach the view', () => {
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)
expect(utils.spy('attachComponentView')).toHaveBeenCalledWith(hostView, 0, createdViews[0]);
expect(renderer.spy('attachComponentView')).toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render);
});
it('should hydrate the dynamic component', () => {
@@ -203,11 +200,12 @@ export function main() {
it('should hydrate the view', () => {
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
});
it('should create and set the render view', () => {
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
expect(renderer.spy('createDynamicComponentView')).toHaveBeenCalledWith(hostView.render, 0, componentProtoView.render);
expect(renderer.spy('createView')).toHaveBeenCalledWith(componentProtoView.render);
expect(createdViews[0].render).toBe(createdRenderViews[0]);
});
@@ -256,7 +254,6 @@ export function main() {
hostView = createView(createProtoView(
[createComponentElBinder(null)]
));
hostView.render = new RenderViewRef();
nestedProtoView = createProtoView();
componentProtoView = createProtoView([
createComponentElBinder(nestedProtoView)
@@ -272,6 +269,7 @@ export function main() {
it('should hydrate the view', () => {
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
});
it('should set the render view', () => {
@@ -309,7 +307,6 @@ export function main() {
));
parentView = createView();
utils.attachComponentView(parentHostView, 0, parentView);
parentView.render = new RenderViewRef();
hostProtoView = createProtoView(
[createComponentElBinder(null)]
);
@@ -326,6 +323,7 @@ export function main() {
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);
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
});
it('should create and set the render view', () => {
@@ -354,7 +352,6 @@ export function main() {
));
parentView = createView();
utils.attachComponentView(parentHostView, 0, parentView);
parentView.render = new RenderViewRef();
hostProtoView = createProtoView(
[createComponentElBinder(null)]
);
@@ -362,25 +359,25 @@ export function main() {
hostRenderViewRef = hostView.render;
});
it('should dehydrate', () => {
it('should detach', () => {
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
expect(utils.spy('detachInPlaceHostView')).toHaveBeenCalledWith(parentView, hostView);
});
it('should detach', () => {
it('should dehydrate', () => {
manager.destroyInPlaceHostView(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);
expect(hostView.render).toBe(null);
});
it('should return the view to the pool', () => {
it('should not return the view to the pool', () => {
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
});
});
@@ -398,7 +395,6 @@ export function main() {
parentView = createView(createProtoView(
[createEmptyElBinder()]
));
parentView.render = new RenderViewRef();
childProtoView = createProtoView();
});
@@ -417,18 +413,19 @@ export function main() {
it('should attach the view', () => {
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null)
expect(utils.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, createdViews[0]);
expect(renderer.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, createdViews[0].render);
});
it('should hydrate the view', () => {
var injector = new Injector([], null, false);
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), injector);
expect(utils.spy('hydrateViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, injector);
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
});
it('should create and set the render view', () => {
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null);
expect(renderer.spy('createViewInContainer')).toHaveBeenCalledWith(
new RenderViewContainerRef(parentView.render, 0), 0, childProtoView.render);
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
expect(createdViews[0].render).toBe(createdRenderViews[0]);
});
@@ -449,7 +446,6 @@ export function main() {
parentView = createView(createProtoView(
[createEmptyElBinder()]
));
parentView.render = new RenderViewRef();
childProtoView = createProtoView();
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
});
@@ -457,17 +453,13 @@ export function main() {
it('should dehydrate', () => {
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
});
it('should detach', () => {
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
});
it('should destroy and clear the render view', () => {
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
expect(renderer.spy('destroyViewInContainer')).toHaveBeenCalledWith(new RenderViewContainerRef(parentView.render, 0), 0);
expect(childView.render).toBe(null);
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
});
it('should return the view to the pool', () => {
@@ -482,7 +474,6 @@ export function main() {
parentView = createView(createProtoView(
[createEmptyElBinder()]
));
parentView.render = new RenderViewRef();
childProtoView = createProtoView();
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
});
@@ -490,17 +481,13 @@ export function main() {
it('should dehydrate', () => {
manager.destroyInPlaceHostView(null, 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));
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
});
it('should not destroy but clear the render view', () => {
manager.destroyInPlaceHostView(null, wrapView(parentView));
expect(renderer.spy('destroyViewInContainer')).not.toHaveBeenCalled();
expect(childView.render).toBe(null);
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
});
it('should return the view to the pool', () => {