66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { CommonModule , NgTemplateOutlet } from '../../index' ;
109import {
1110 Component ,
1211 ContentChildren ,
@@ -17,12 +16,16 @@ import {
1716 Injector ,
1817 NO_ERRORS_SCHEMA ,
1918 OnDestroy ,
19+ Optional ,
2020 Provider ,
2121 QueryList ,
22+ SkipSelf ,
2223 TemplateRef ,
24+ inject ,
2325} from '@angular/core' ;
2426import { ComponentFixture , TestBed } from '@angular/core/testing' ;
2527import { expect } from '@angular/private/testing/matchers' ;
28+ import { CommonModule , NgTemplateOutlet } from '../../index' ;
2629
2730describe ( 'NgTemplateOutlet' , ( ) => {
2831 let fixture : ComponentFixture < any > ;
@@ -49,6 +52,8 @@ describe('NgTemplateOutlet', () => {
4952 DestroyableCmpt ,
5053 MultiContextComponent ,
5154 InjectValueComponent ,
55+ ProvideValueComponent ,
56+ NestingCounter ,
5257 ] ,
5358 imports : [ CommonModule ] ,
5459 providers : [ DestroyedSpyService ] ,
@@ -360,6 +365,43 @@ describe('NgTemplateOutlet', () => {
360365 detectChangesAndExpectText ( 'Hello world' ) ;
361366 } ) ;
362367
368+ it ( 'should be able to inherit outlet injector' , ( ) => {
369+ const template = `
370+ <ng-template #tpl><inject-value></inject-value></ng-template>
371+ <provide-value>
372+ <ng-container *ngTemplateOutlet="tpl; injector: 'outlet'"></ng-container>
373+ </provide-value>
374+ ` ;
375+ fixture = createTestComponent ( template , [ { provide : templateToken , useValue : 'root' } ] ) ;
376+ detectChangesAndExpectText ( 'Hello provide-value' ) ;
377+ } ) ;
378+
379+ it ( 'should be able to inherit outlet injector in a deeply nested structure' , ( ) => {
380+ // This template should create the following rendered structure
381+ // (Spaces & newlines added for readability):
382+ // <nesting-counter> 1
383+ // <nesting counter> 2
384+ // <nesting-counter> 3
385+ // <nesting-counter> 4 </nesting-counter>
386+ // </nesting-counter>
387+ // </nesting-counter>
388+ // <nesting-counter> 2 </nesting-counter>
389+ // </nesting-counter>
390+ const template = `
391+ <ng-container *ngTemplateOutlet="node; context: {$implicit: [[[[]]], []]}" />
392+
393+ <ng-template #node let-data>
394+ <nesting-counter>
395+ @for (item of data; track $index) {
396+ <ng-container *ngTemplateOutlet="node; context: {$implicit: item}; injector: 'outlet'" />
397+ }
398+ </nesting-counter>
399+ </ng-template>
400+ ` ;
401+ fixture = createTestComponent ( template ) ;
402+ detectChangesAndExpectText ( '12342' ) ;
403+ } ) ;
404+
363405 it ( 'should be available as a standalone directive' , ( ) => {
364406 @Component ( {
365407 selector : 'test-component' ,
@@ -443,6 +485,14 @@ class TestComponent {
443485 injector : Injector | null = null ;
444486}
445487
488+ @Component ( {
489+ selector : 'provide-value' ,
490+ template : '<ng-content />' ,
491+ providers : [ { provide : templateToken , useValue : 'provide-value' } ] ,
492+ standalone : false ,
493+ } )
494+ class ProvideValueComponent { }
495+
446496@Component ( {
447497 selector : 'inject-value' ,
448498 template : 'Hello {{tokenValue}}' ,
@@ -466,6 +516,24 @@ class MultiContextComponent {
466516 context2 : { name : string } | undefined ;
467517}
468518
519+ const NESTING_DEPTH = new InjectionToken < number > ( 'NESTING_DEPTH' ) ;
520+
521+ @Component ( {
522+ selector : 'nesting-counter' ,
523+ template : '{{depth}}<ng-content />' ,
524+ providers : [
525+ {
526+ provide : NESTING_DEPTH ,
527+ useFactory : ( l : number ) => ( l ? l + 1 : 1 ) ,
528+ deps : [ [ new Optional ( ) , new SkipSelf ( ) , NESTING_DEPTH ] ] ,
529+ } ,
530+ ] ,
531+ standalone : false ,
532+ } )
533+ class NestingCounter {
534+ depth = inject ( NESTING_DEPTH ) ;
535+ }
536+
469537function createTestComponent (
470538 template : string ,
471539 providers : Provider [ ] = [ ] ,
0 commit comments