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
					
				
							
								
								
									
										3
									
								
								modules/angular2/src/render/dom/view/view.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								modules/angular2/src/render/dom/view/view.js
									
									
									
									
										vendored
									
									
								
							| @ -69,6 +69,9 @@ export class RenderView { | |||||||
|     this.componentChildViews[elementIndex] = childView; |     this.componentChildViews[elementIndex] = childView; | ||||||
|     if (this._hydrated) { |     if (this._hydrated) { | ||||||
|       childView.hydrate(lightDom); |       childView.hydrate(lightDom); | ||||||
|  |       if (isPresent(lightDom)) { | ||||||
|  |         lightDom.redistribute(); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ import {IntegrationTestbed, LoggingEventDispatcher, FakeEvent} from './integrati | |||||||
| 
 | 
 | ||||||
| export function main() { | export function main() { | ||||||
|   describe('DirectDomRenderer integration', () => { |   describe('DirectDomRenderer integration', () => { | ||||||
|     var testbed, renderer, eventPlugin, compile, rootEl; |     var testbed, renderer, eventPlugin, compileRoot, rootEl; | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       rootEl = el('<div></div>'); |       rootEl = el('<div></div>'); | ||||||
| @ -36,7 +36,7 @@ export function main() { | |||||||
|       }); |       }); | ||||||
|       renderer = testbed.renderer; |       renderer = testbed.renderer; | ||||||
|       eventPlugin = testbed.eventPlugin; |       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) => { |     it('should create root views while using the given elements in place', inject([AsyncTestCompleter], (async) => { | ||||||
| @ -93,7 +93,7 @@ export function main() { | |||||||
|           directives: [] |           directives: [] | ||||||
|         })] |         })] | ||||||
|       }); |       }); | ||||||
|       compile(rootEl, 'someComponent').then( (rootProtoView) => { |       compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { | ||||||
|         var viewRefs = renderer.createView(rootProtoView.render); |         var viewRefs = renderer.createView(rootProtoView.render); | ||||||
|         renderer.setText(viewRefs[1], 0, 'hello'); |         renderer.setText(viewRefs[1], 0, 'hello'); | ||||||
|         expect(rootEl).toHaveText('hello'); |         expect(rootEl).toHaveText('hello'); | ||||||
| @ -109,7 +109,7 @@ export function main() { | |||||||
|           directives: [] |           directives: [] | ||||||
|         })] |         })] | ||||||
|       }); |       }); | ||||||
|       compile(rootEl, 'someComponent').then( (rootProtoView) => { |       compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { | ||||||
|         var viewRefs = renderer.createView(rootProtoView.render); |         var viewRefs = renderer.createView(rootProtoView.render); | ||||||
|         renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello'); |         renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello'); | ||||||
|         expect(DOM.childNodes(rootEl)[0].value).toEqual('hello'); |         expect(DOM.childNodes(rootEl)[0].value).toEqual('hello'); | ||||||
| @ -125,7 +125,7 @@ export function main() { | |||||||
|           directives: [] |           directives: [] | ||||||
|         })] |         })] | ||||||
|       }); |       }); | ||||||
|       compile(rootEl, 'someComponent').then( (rootProtoView) => { |       compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { | ||||||
|         var viewRef = renderer.createView(rootProtoView.render)[1]; |         var viewRef = renderer.createView(rootProtoView.render)[1]; | ||||||
|         var vcProtoViewRef = rootProtoView.elementBinders[0] |         var vcProtoViewRef = rootProtoView.elementBinders[0] | ||||||
|           .nestedProtoView.elementBinders[0].nestedProtoView.render; |           .nestedProtoView.elementBinders[0].nestedProtoView.render; | ||||||
| @ -151,7 +151,7 @@ export function main() { | |||||||
|         })], |         })], | ||||||
|         viewCacheCapacity: 2 |         viewCacheCapacity: 2 | ||||||
|       }); |       }); | ||||||
|       compile(rootEl, 'someComponent').then( (rootProtoView) => { |       compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { | ||||||
|         var vcProtoViewRef = rootProtoView.elementBinders[0] |         var vcProtoViewRef = rootProtoView.elementBinders[0] | ||||||
|           .nestedProtoView.elementBinders[0].nestedProtoView.render; |           .nestedProtoView.elementBinders[0].nestedProtoView.render; | ||||||
| 
 | 
 | ||||||
| @ -176,7 +176,7 @@ export function main() { | |||||||
|           directives: [] |           directives: [] | ||||||
|         })] |         })] | ||||||
|       }); |       }); | ||||||
|       compile(rootEl, 'someComponent').then( (rootProtoView) => { |       compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { | ||||||
|         var viewRef = renderer.createView(rootProtoView.render)[1]; |         var viewRef = renderer.createView(rootProtoView.render)[1]; | ||||||
|         var dispatcher = new LoggingEventDispatcher(); |         var dispatcher = new LoggingEventDispatcher(); | ||||||
|         renderer.setEventDispatcher(viewRef, dispatcher); |         renderer.setEventDispatcher(viewRef, dispatcher); | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ export class IntegrationTestbed { | |||||||
|     this.renderer = new DirectDomRenderer(compiler, viewFactory, shadowDomStrategy); |     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.renderer.createRootProtoView(rootEl, componentId).then( (rootProtoView) => { | ||||||
|       return this._compileNestedProtoViews(rootProtoView, [ |       return this._compileNestedProtoViews(rootProtoView, [ | ||||||
|         new DirectiveMetadata({ |         new DirectiveMetadata({ | ||||||
| @ -59,9 +59,13 @@ export class IntegrationTestbed { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _compile(template):Promise<ProtoViewDto> { |   compile(componentId):Promise<ProtoViewDto> { | ||||||
|     return this.renderer.compile(template).then( (protoView) => { |     var childTemplate = MapWrapper.get(this._templates, componentId); | ||||||
|       return this._compileNestedProtoViews(protoView, template.directives); |     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)) { |       if (isPresent(nestedComponentId)) { | ||||||
|         var childTemplate = MapWrapper.get(this._templates, nestedComponentId); |         var childTemplate = MapWrapper.get(this._templates, nestedComponentId); | ||||||
|         if (isBlank(childTemplate)) { |         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)) { |       } else if (isPresent(elementBinder.nestedProtoView)) { | ||||||
|         nestedCall = this._compileNestedProtoViews(elementBinder.nestedProtoView, directives); |         nestedCall = this._compileNestedProtoViews(elementBinder.nestedProtoView, directives); | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|       describe(`${name} shadow dom strategy`, () => { |       describe(`${name} shadow dom strategy`, () => { | ||||||
| 
 | 
 | ||||||
|         var testbed, renderer, rootEl, compile; |         var testbed, renderer, rootEl, compile, compileRoot; | ||||||
| 
 | 
 | ||||||
|         function createRenderer({templates}) { |         function createRenderer({templates}) { | ||||||
|           testbed = new IntegrationTestbed({ |           testbed = new IntegrationTestbed({ | ||||||
| @ -60,7 +60,8 @@ export function main() { | |||||||
|             templates: ListWrapper.concat(templates, componentTemplates) |             templates: ListWrapper.concat(templates, componentTemplates) | ||||||
|           }); |           }); | ||||||
|           renderer = testbed.renderer; |           renderer = testbed.renderer; | ||||||
|           compile = (rootEl, componentId) => testbed.compile(rootEl, componentId); |           compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId); | ||||||
|  |           compile = (componentId) => testbed.compile(componentId); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         beforeEach( () => { |         beforeEach( () => { | ||||||
| @ -77,7 +78,7 @@ export function main() { | |||||||
|               directives: [simple] |               directives: [simple] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             renderer.createView(pv.render); |             renderer.createView(pv.render); | ||||||
| 
 | 
 | ||||||
|             expect(rootEl).toHaveText('SIMPLE(A)'); |             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) => { |         it('should support multiple content tags', inject([AsyncTestCompleter], (async) => { | ||||||
|           createRenderer({ |           createRenderer({ | ||||||
|             templates: [new ViewDefinition({ |             templates: [new ViewDefinition({ | ||||||
| @ -98,7 +122,7 @@ export function main() { | |||||||
|               directives: [multipleContentTagsComponent] |               directives: [multipleContentTagsComponent] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             renderer.createView(pv.render); |             renderer.createView(pv.render); | ||||||
| 
 | 
 | ||||||
|             expect(rootEl).toHaveText('(A, BC)'); |             expect(rootEl).toHaveText('(A, BC)'); | ||||||
| @ -118,7 +142,7 @@ export function main() { | |||||||
|               directives: [multipleContentTagsComponent] |               directives: [multipleContentTagsComponent] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             renderer.createView(pv.render); |             renderer.createView(pv.render); | ||||||
| 
 | 
 | ||||||
|             expect(rootEl).toHaveText('(, BAC)'); |             expect(rootEl).toHaveText('(, BAC)'); | ||||||
| @ -138,7 +162,7 @@ export function main() { | |||||||
|               directives: [multipleContentTagsComponent, manualViewportDirective] |               directives: [multipleContentTagsComponent, manualViewportDirective] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             var viewRefs = renderer.createView(pv.render); |             var viewRefs = renderer.createView(pv.render); | ||||||
|             var vcRef = new ViewContainerRef(viewRefs[1], 1); |             var vcRef = new ViewContainerRef(viewRefs[1], 1); | ||||||
|             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView |             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView | ||||||
| @ -170,7 +194,7 @@ export function main() { | |||||||
|               directives: [multipleContentTagsComponent, manualViewportDirective] |               directives: [multipleContentTagsComponent, manualViewportDirective] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             var viewRefs = renderer.createView(pv.render); |             var viewRefs = renderer.createView(pv.render); | ||||||
|             var vcRef = new ViewContainerRef(viewRefs[1], 1); |             var vcRef = new ViewContainerRef(viewRefs[1], 1); | ||||||
|             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView |             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView | ||||||
| @ -202,7 +226,7 @@ export function main() { | |||||||
|               directives: [outerWithIndirectNestedComponent] |               directives: [outerWithIndirectNestedComponent] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             renderer.createView(pv.render); |             renderer.createView(pv.render); | ||||||
| 
 | 
 | ||||||
|             expect(rootEl).toHaveText('OUTER(SIMPLE(AB))'); |             expect(rootEl).toHaveText('OUTER(SIMPLE(AB))'); | ||||||
| @ -223,7 +247,7 @@ export function main() { | |||||||
|               directives: [outerComponent, manualViewportDirective] |               directives: [outerComponent, manualViewportDirective] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             var viewRefs = renderer.createView(pv.render); |             var viewRefs = renderer.createView(pv.render); | ||||||
|             var vcRef = new ViewContainerRef(viewRefs[1], 1); |             var vcRef = new ViewContainerRef(viewRefs[1], 1); | ||||||
|             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView |             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView | ||||||
| @ -251,7 +275,7 @@ export function main() { | |||||||
|               directives: [conditionalContentComponent] |               directives: [conditionalContentComponent] | ||||||
|             })] |             })] | ||||||
|           }); |           }); | ||||||
|           compile(rootEl, 'main').then( (pv) => { |           compileRoot(rootEl, 'main').then( (pv) => { | ||||||
|             var viewRefs = renderer.createView(pv.render); |             var viewRefs = renderer.createView(pv.render); | ||||||
|             var vcRef = new ViewContainerRef(viewRefs[2], 0); |             var vcRef = new ViewContainerRef(viewRefs[2], 0); | ||||||
|             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView |             var vcProtoViewRef = pv.elementBinders[0].nestedProtoView | ||||||
| @ -302,6 +326,12 @@ var simple = new DirectiveMetadata({ | |||||||
|   type: DirectiveMetadata.COMPONENT_TYPE |   type: DirectiveMetadata.COMPONENT_TYPE | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | var dynamicComponent = new DirectiveMetadata({ | ||||||
|  |   selector: 'dynamic', | ||||||
|  |   id: 'dynamic', | ||||||
|  |   type: DirectiveMetadata.COMPONENT_TYPE | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| var multipleContentTagsComponent = new DirectiveMetadata({ | var multipleContentTagsComponent = new DirectiveMetadata({ | ||||||
|   selector: 'multiple-content-tags', |   selector: 'multiple-content-tags', | ||||||
|   id: 'multiple-content-tags', |   id: 'multiple-content-tags', | ||||||
|  | |||||||
							
								
								
									
										76
									
								
								modules/angular2/test/render/dom/view/view_spec.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								modules/angular2/test/render/dom/view/view_spec.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user