@@ -7193,4 +7193,129 @@ describe('di', () => {
71937193 ) ;
71947194 } ) ;
71957195 } ) ;
7196+
7197+ describe ( 'control flow + viewProviders' , ( ) => {
7198+ it ( 'should not allow projected content to see viewProviders when wrapped in @if' , ( ) => {
7199+ const token = new InjectionToken < string > ( 'token' ) ;
7200+
7201+ @Component ( {
7202+ selector : 'app-child-component' ,
7203+ template : '{{value}}' ,
7204+ } )
7205+ class ChildComponent {
7206+ value = inject ( token ) ;
7207+ }
7208+
7209+ @Component ( {
7210+ selector : 'app-provider-component' ,
7211+ providers : [ { provide : token , useValue : 'provider' } ] ,
7212+ viewProviders : [ { provide : token , useValue : 'viewProvider' } ] ,
7213+ template : `
7214+ <div>
7215+ Projected<br />
7216+ <ng-content></ng-content>
7217+ </div>
7218+ ` ,
7219+ } )
7220+ class ProviderComponent { }
7221+
7222+ @Component ( {
7223+ selector : 'app-test-new-flow' ,
7224+ imports : [ ProviderComponent , ChildComponent ] ,
7225+ template : `
7226+ <app-provider-component>
7227+ @if (true) {
7228+ <app-child-component />
7229+ }
7230+ </app-provider-component>
7231+ ` ,
7232+ } )
7233+ class TestNewFlowComponent { }
7234+
7235+ @Component ( {
7236+ selector : 'app-test-old-flow' ,
7237+ imports : [ ProviderComponent , ChildComponent , CommonModule ] ,
7238+ template : `
7239+ <app-provider-component>
7240+ <app-child-component *ngIf="flag" />
7241+ </app-provider-component>
7242+ ` ,
7243+ } )
7244+ class TestOldFlowComponent {
7245+ flag = true ;
7246+ }
7247+
7248+ // Test old syntax it's ok
7249+ const oldFixture = TestBed . createComponent ( TestOldFlowComponent ) ;
7250+ oldFixture . detectChanges ( ) ;
7251+ expect ( oldFixture . nativeElement . textContent ) . toContain ( 'provider' ) ;
7252+ expect ( oldFixture . nativeElement . textContent ) . not . toContain ( 'viewProvider' ) ;
7253+
7254+ // Test that the new syntax behaves like the old one
7255+ const newFixture = TestBed . createComponent ( TestNewFlowComponent ) ;
7256+ newFixture . detectChanges ( ) ;
7257+ expect ( newFixture . nativeElement . textContent ) . toContain ( 'provider' ) ;
7258+ expect ( newFixture . nativeElement . textContent ) . not . toContain ( 'viewProvider' ) ;
7259+ } ) ;
7260+
7261+ it ( 'should allow a directive in an @if block to inject a token from viewProviders' , ( ) => {
7262+ const TOKEN = new InjectionToken < string > ( 'token' ) ;
7263+
7264+ @Directive ( {
7265+ selector : '[testDir]' ,
7266+ } )
7267+ class TestDir {
7268+ value = inject ( TOKEN ) ;
7269+ }
7270+
7271+ @Component ( {
7272+ selector : 'test-comp' ,
7273+ imports : [ TestDir ] ,
7274+ viewProviders : [ { provide : TOKEN , useValue : 'view-value' } ] ,
7275+ template : `
7276+ @if (true) {
7277+ <div testDir></div>
7278+ }
7279+ ` ,
7280+ } )
7281+ class TestComp {
7282+ @ViewChild ( TestDir ) testDir ! : TestDir ;
7283+ }
7284+
7285+ const fixture = TestBed . createComponent ( TestComp ) ;
7286+ fixture . detectChanges ( ) ;
7287+
7288+ expect ( fixture . componentInstance . testDir . value ) . toBe ( 'view-value' ) ;
7289+ } ) ;
7290+
7291+ it ( 'should allow a directive in nested @if blocks to inject a token from viewProviders' , ( ) => {
7292+ const TOKEN = new InjectionToken < string > ( 'token' ) ;
7293+
7294+ @Directive ( {
7295+ selector : '[testDir]' ,
7296+ } )
7297+ class TestDir {
7298+ value = inject ( TOKEN ) ;
7299+ }
7300+
7301+ @Component ( {
7302+ selector : 'test-comp' ,
7303+ imports : [ TestDir ] ,
7304+ viewProviders : [ { provide : TOKEN , useValue : 'view-value' } ] ,
7305+ template : `
7306+ @if (true) {
7307+ <div testDir></div>
7308+ }
7309+ ` ,
7310+ } )
7311+ class TestComp {
7312+ @ViewChild ( TestDir ) testDir ! : TestDir ;
7313+ }
7314+
7315+ const fixture = TestBed . createComponent ( TestComp ) ;
7316+ fixture . detectChanges ( ) ;
7317+
7318+ expect ( fixture . componentInstance . testDir . value ) . toBe ( 'view-value' ) ;
7319+ } ) ;
7320+ } ) ;
71967321} ) ;
0 commit comments