fix(ivy): update ViewContainerRef to get the correct parentInjector (#35013)
PR Close #35013
This commit is contained in:
		
							parent
							
								
									519e9e1ae8
								
							
						
					
					
						commit
						b8ffcf973c
					
				| @ -12,8 +12,10 @@ import {DECLARATION_VIEW, LView, T_HOST} from './interfaces/view'; | ||||
| import {getParentInjectorViewOffset} from './util/injector_utils'; | ||||
| 
 | ||||
| /** | ||||
|  * Unwraps a parent injector location number to find the view offset from the current injector, | ||||
|  * then walks up the declaration view tree until the TNode of the parent injector is found. | ||||
|  * If `startTNode.parent` exists and has an injector, returns TNode for that injector. | ||||
|  * Otherwise, unwraps a parent injector location number to find the view offset from the current | ||||
|  * injector, then walks up the declaration view tree until the TNode of the parent injector is | ||||
|  * found. | ||||
|  * | ||||
|  * @param location The location of the parent injector, which contains the view offset | ||||
|  * @param startView The LView instance from which to start walking up the view tree | ||||
| @ -23,14 +25,17 @@ import {getParentInjectorViewOffset} from './util/injector_utils'; | ||||
| export function getParentInjectorTNode( | ||||
|     location: RelativeInjectorLocation, startView: LView, startTNode: TNode): TElementNode| | ||||
|     TContainerNode|null { | ||||
|   // If there is an injector on the parent TNode, retrieve the TNode for that injector.
 | ||||
|   if (startTNode.parent && startTNode.parent.injectorIndex !== -1) { | ||||
|     // view offset is 0
 | ||||
|     const injectorIndex = startTNode.parent.injectorIndex; | ||||
|     let parentTNode = startTNode.parent; | ||||
|     while (parentTNode.parent != null && injectorIndex == parentTNode.injectorIndex) { | ||||
|       parentTNode = parentTNode.parent; | ||||
|     let tNode = startTNode.parent; | ||||
|     // If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent
 | ||||
|     // injector.
 | ||||
|     while (tNode.parent != null && injectorIndex == tNode.parent.injectorIndex) { | ||||
|       tNode = tNode.parent; | ||||
|     } | ||||
|     return parentTNode; | ||||
|     return tNode; | ||||
|   } | ||||
|   let viewOffset = getParentInjectorViewOffset(location); | ||||
|   // view offset is 1
 | ||||
|  | ||||
| @ -470,6 +470,64 @@ describe('di', () => { | ||||
|         fixture.detectChanges(); | ||||
|         expect(divElement.id).toBe('bar'); | ||||
|       }); | ||||
| 
 | ||||
|       it('dynamic components should find dependencies when parent is projected', () => { | ||||
|         @Directive({selector: '[dirA]'}) | ||||
|         class DirA { | ||||
|         } | ||||
|         @Directive({selector: '[dirB]'}) | ||||
|         class DirB { | ||||
|         } | ||||
|         @Component({selector: 'child', template: ''}) | ||||
|         class Child { | ||||
|           constructor(@Optional() readonly dirA: DirA, @Optional() readonly dirB: DirB) {} | ||||
|         } | ||||
|         @Component({ | ||||
|           selector: 'projector', | ||||
|           template: '<ng-content></ng-content>', | ||||
|         }) | ||||
|         class Projector { | ||||
|         } | ||||
| 
 | ||||
|         @Component({ | ||||
|           template: ` | ||||
|           <projector> | ||||
|             <div dirA> | ||||
|               <ng-container #childOrigin></ng-container> | ||||
|               <ng-container #childOriginWithDirB dirB></ng-container> | ||||
|             </div> | ||||
|           </projector>`,
 | ||||
|           entryComponents: [Child] | ||||
|         }) | ||||
|         class MyApp { | ||||
|           @ViewChild('childOrigin', {read: ViewContainerRef, static: true}) | ||||
|           childOrigin !: ViewContainerRef; | ||||
|           @ViewChild('childOriginWithDirB', {read: ViewContainerRef, static: true}) | ||||
|           childOriginWithDirB !: ViewContainerRef; | ||||
|           childFactory = this.resolver.resolveComponentFactory(Child); | ||||
| 
 | ||||
|           constructor(readonly resolver: ComponentFactoryResolver, readonly injector: Injector) {} | ||||
| 
 | ||||
|           addChild() { return this.childOrigin.createComponent(this.childFactory); } | ||||
|           addChildWithDirB() { return this.childOriginWithDirB.createComponent(this.childFactory); } | ||||
|         } | ||||
| 
 | ||||
|         const fixture = | ||||
|             TestBed.configureTestingModule({declarations: [Child, DirA, DirB, Projector, MyApp]}) | ||||
|                 .createComponent(MyApp); | ||||
|         const child = fixture.componentInstance.addChild(); | ||||
|         expect(child).toBeDefined(); | ||||
|         expect(child.instance.dirA) | ||||
|             .not.toBeNull('dirA should be found. It is on the parent of the viewContainerRef.'); | ||||
|         const child2 = fixture.componentInstance.addChildWithDirB(); | ||||
|         expect(child2).toBeDefined(); | ||||
|         expect(child2.instance.dirA) | ||||
|             .not.toBeNull('dirA should be found. It is on the parent of the viewContainerRef.'); | ||||
|         expect(child2.instance.dirB) | ||||
|             .toBeNull( | ||||
|                 'dirB appears on the ng-container and should not be found because the ' + | ||||
|                 'viewContainerRef.createComponent node is inserted next to the container.'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     it('should throw if directive is not found anywhere', () => { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user