fix(shadow_dom): redistribute light dom when a dynamic component is attached.
Fixes #1077 Closes #1315
This commit is contained in:
parent
daf0f472b3
commit
8499cf84c3
|
@ -69,6 +69,9 @@ export class RenderView {
|
|||
this.componentChildViews[elementIndex] = childView;
|
||||
if (this._hydrated) {
|
||||
childView.hydrate(lightDom);
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import {IntegrationTestbed, LoggingEventDispatcher, FakeEvent} from './integrati
|
|||
|
||||
export function main() {
|
||||
describe('DirectDomRenderer integration', () => {
|
||||
var testbed, renderer, eventPlugin, compile, rootEl;
|
||||
var testbed, renderer, eventPlugin, compileRoot, rootEl;
|
||||
|
||||
beforeEach(() => {
|
||||
rootEl = el('<div></div>');
|
||||
|
@ -36,7 +36,7 @@ export function main() {
|
|||
});
|
||||
renderer = testbed.renderer;
|
||||
eventPlugin = testbed.eventPlugin;
|
||||
compile = (rootEl, componentId) => testbed.compile(rootEl, componentId);
|
||||
compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId);
|
||||
}
|
||||
|
||||
it('should create root views while using the given elements in place', inject([AsyncTestCompleter], (async) => {
|
||||
|
@ -93,7 +93,7 @@ export function main() {
|
|||
directives: []
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
var viewRefs = renderer.createView(rootProtoView.render);
|
||||
renderer.setText(viewRefs[1], 0, 'hello');
|
||||
expect(rootEl).toHaveText('hello');
|
||||
|
@ -109,7 +109,7 @@ export function main() {
|
|||
directives: []
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
var viewRefs = renderer.createView(rootProtoView.render);
|
||||
renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello');
|
||||
expect(DOM.childNodes(rootEl)[0].value).toEqual('hello');
|
||||
|
@ -125,7 +125,7 @@ export function main() {
|
|||
directives: []
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
var viewRef = renderer.createView(rootProtoView.render)[1];
|
||||
var vcProtoViewRef = rootProtoView.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0].nestedProtoView.render;
|
||||
|
@ -151,7 +151,7 @@ export function main() {
|
|||
})],
|
||||
viewCacheCapacity: 2
|
||||
});
|
||||
compile(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
var vcProtoViewRef = rootProtoView.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0].nestedProtoView.render;
|
||||
|
||||
|
@ -176,7 +176,7 @@ export function main() {
|
|||
directives: []
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
|
||||
var viewRef = renderer.createView(rootProtoView.render)[1];
|
||||
var dispatcher = new LoggingEventDispatcher();
|
||||
renderer.setEventDispatcher(viewRef, dispatcher);
|
||||
|
|
|
@ -48,7 +48,7 @@ export class IntegrationTestbed {
|
|||
this.renderer = new DirectDomRenderer(compiler, viewFactory, shadowDomStrategy);
|
||||
}
|
||||
|
||||
compile(rootEl, componentId):Promise<ProtoViewDto> {
|
||||
compileRoot(rootEl, componentId):Promise<ProtoViewDto> {
|
||||
return this.renderer.createRootProtoView(rootEl, componentId).then( (rootProtoView) => {
|
||||
return this._compileNestedProtoViews(rootProtoView, [
|
||||
new DirectiveMetadata({
|
||||
|
@ -59,9 +59,13 @@ export class IntegrationTestbed {
|
|||
});
|
||||
}
|
||||
|
||||
_compile(template):Promise<ProtoViewDto> {
|
||||
return this.renderer.compile(template).then( (protoView) => {
|
||||
return this._compileNestedProtoViews(protoView, template.directives);
|
||||
compile(componentId):Promise<ProtoViewDto> {
|
||||
var childTemplate = MapWrapper.get(this._templates, componentId);
|
||||
if (isBlank(childTemplate)) {
|
||||
throw new BaseException(`No template for component ${componentId}`);
|
||||
}
|
||||
return this.renderer.compile(childTemplate).then( (protoView) => {
|
||||
return this._compileNestedProtoViews(protoView, childTemplate.directives);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -80,9 +84,11 @@ export class IntegrationTestbed {
|
|||
if (isPresent(nestedComponentId)) {
|
||||
var childTemplate = MapWrapper.get(this._templates, nestedComponentId);
|
||||
if (isBlank(childTemplate)) {
|
||||
throw new BaseException(`Could not find template for ${nestedComponentId}!`);
|
||||
// dynamic component
|
||||
ListWrapper.push(childComponentRenderPvRefs, null);
|
||||
} else {
|
||||
nestedCall = this.compile(nestedComponentId);
|
||||
}
|
||||
nestedCall = this._compile(childTemplate);
|
||||
} else if (isPresent(elementBinder.nestedProtoView)) {
|
||||
nestedCall = this._compileNestedProtoViews(elementBinder.nestedProtoView, directives);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export function main() {
|
|||
|
||||
describe(`${name} shadow dom strategy`, () => {
|
||||
|
||||
var testbed, renderer, rootEl, compile;
|
||||
var testbed, renderer, rootEl, compile, compileRoot;
|
||||
|
||||
function createRenderer({templates}) {
|
||||
testbed = new IntegrationTestbed({
|
||||
|
@ -60,7 +60,8 @@ export function main() {
|
|||
templates: ListWrapper.concat(templates, componentTemplates)
|
||||
});
|
||||
renderer = testbed.renderer;
|
||||
compile = (rootEl, componentId) => testbed.compile(rootEl, componentId);
|
||||
compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId);
|
||||
compile = (componentId) => testbed.compile(componentId);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
|
@ -77,7 +78,7 @@ export function main() {
|
|||
directives: [simple]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
renderer.createView(pv.render);
|
||||
|
||||
expect(rootEl).toHaveText('SIMPLE(A)');
|
||||
|
@ -86,6 +87,29 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support dynamic components', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<dynamic>' +
|
||||
'<div>A</div>' +
|
||||
'</dynamic>',
|
||||
directives: [dynamicComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(rootEl, 'main').then( (rootPv) => {
|
||||
compile('simple').then( (simplePv) => {
|
||||
var views = renderer.createView(rootPv.render);
|
||||
var simpleViews = renderer.createView(simplePv.render);
|
||||
renderer.setDynamicComponentView(views[1], 0, simpleViews[0]);
|
||||
|
||||
expect(rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support multiple content tags', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
|
@ -98,7 +122,7 @@ export function main() {
|
|||
directives: [multipleContentTagsComponent]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
renderer.createView(pv.render);
|
||||
|
||||
expect(rootEl).toHaveText('(A, BC)');
|
||||
|
@ -118,7 +142,7 @@ export function main() {
|
|||
directives: [multipleContentTagsComponent]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
renderer.createView(pv.render);
|
||||
|
||||
expect(rootEl).toHaveText('(, BAC)');
|
||||
|
@ -138,7 +162,7 @@ export function main() {
|
|||
directives: [multipleContentTagsComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
var viewRefs = renderer.createView(pv.render);
|
||||
var vcRef = new ViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
|
@ -170,7 +194,7 @@ export function main() {
|
|||
directives: [multipleContentTagsComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
var viewRefs = renderer.createView(pv.render);
|
||||
var vcRef = new ViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
|
@ -202,7 +226,7 @@ export function main() {
|
|||
directives: [outerWithIndirectNestedComponent]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
renderer.createView(pv.render);
|
||||
|
||||
expect(rootEl).toHaveText('OUTER(SIMPLE(AB))');
|
||||
|
@ -223,7 +247,7 @@ export function main() {
|
|||
directives: [outerComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
var viewRefs = renderer.createView(pv.render);
|
||||
var vcRef = new ViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
|
@ -251,7 +275,7 @@ export function main() {
|
|||
directives: [conditionalContentComponent]
|
||||
})]
|
||||
});
|
||||
compile(rootEl, 'main').then( (pv) => {
|
||||
compileRoot(rootEl, 'main').then( (pv) => {
|
||||
var viewRefs = renderer.createView(pv.render);
|
||||
var vcRef = new ViewContainerRef(viewRefs[2], 0);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
|
@ -302,6 +326,12 @@ var simple = new DirectiveMetadata({
|
|||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var dynamicComponent = new DirectiveMetadata({
|
||||
selector: 'dynamic',
|
||||
id: 'dynamic',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var multipleContentTagsComponent = new DirectiveMetadata({
|
||||
selector: 'multiple-content-tags',
|
||||
id: 'multiple-content-tags',
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, el} from 'angular2/test_lib';
|
||||
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {RenderView} from 'angular2/src/render/dom/view/view';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
||||
|
||||
export function main() {
|
||||
|
||||
function createView() {
|
||||
var proto = null;
|
||||
var rootNodes = [el('<div></div>')];
|
||||
var boundTextNodes = [];
|
||||
var boundElements = [el('<div></div>')];
|
||||
var viewContainers = [];
|
||||
var contentTags = [];
|
||||
return new RenderView(proto, rootNodes,
|
||||
boundTextNodes, boundElements, viewContainers, contentTags);
|
||||
}
|
||||
|
||||
function createShadowDomStrategy(log) {
|
||||
return new FakeShadowDomStrategy(log);
|
||||
}
|
||||
|
||||
describe('RenderView', () => {
|
||||
var log, strategy;
|
||||
|
||||
beforeEach( () => {
|
||||
log = [];
|
||||
strategy = createShadowDomStrategy(log);
|
||||
});
|
||||
|
||||
describe('setComponentView', () => {
|
||||
|
||||
it('should redistribute when a component is added to a hydrated view', () => {
|
||||
var hostView = createView();
|
||||
var childView = createView();
|
||||
hostView.hydrate(null);
|
||||
hostView.setComponentView(strategy, 0, childView);
|
||||
expect(log[0]).toEqual(['redistribute']);
|
||||
});
|
||||
|
||||
it('should not redistribute when a component is added to a dehydrated view', () => {
|
||||
var hostView = createView();
|
||||
var childView = createView();
|
||||
hostView.setComponentView(strategy, 0, childView);
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class FakeShadowDomStrategy extends ShadowDomStrategy {
|
||||
log;
|
||||
constructor(log) {
|
||||
super();
|
||||
this.log = log;
|
||||
}
|
||||
constructLightDom(lightDomView:RenderView, shadowDomView:RenderView, element): LightDom {
|
||||
return new FakeLightDom(this.log, lightDomView, shadowDomView, element);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeLightDom extends LightDom {
|
||||
log;
|
||||
constructor(log, lightDomView:RenderView, shadowDomView:RenderView, element) {
|
||||
super(lightDomView, shadowDomView, element);
|
||||
this.log = log;
|
||||
}
|
||||
redistribute() {
|
||||
ListWrapper.push(this.log, ['redistribute']);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue