77 */
88import { animate , AnimationPlayer , AnimationTriggerMetadata , state , style , transition , trigger } from '@angular/animations' ;
99import { ɵAnimationEngine as AnimationEngine } from '@angular/animations/browser' ;
10- import { Component , destroyPlatform , Injectable , NgModule , NgZone , RendererFactory2 , RendererType2 , ViewChild } from '@angular/core' ;
10+ import { APP_INITIALIZER , Component , destroyPlatform , importProvidersFrom , Injectable , NgModule , NgZone , RendererFactory2 , RendererType2 , ViewChild } from '@angular/core' ;
1111import { TestBed } from '@angular/core/testing' ;
12+ import { bootstrapApplication } from '@angular/platform-browser' ;
1213import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' ;
1314import { BrowserAnimationsModule , ɵAnimationRendererFactory as AnimationRendererFactory , ɵInjectableAnimationEngine as InjectableAnimationEngine } from '@angular/platform-browser/animations' ;
1415import { DomRendererFactory2 } from '@angular/platform-browser/src/dom/dom_renderer' ;
@@ -330,7 +331,8 @@ describe('destroy', () => {
330331 beforeEach ( destroyPlatform ) ;
331332 afterEach ( destroyPlatform ) ;
332333
333- it ( 'should clear bootstrapped component contents' ,
334+ // See https://github.com/angular/angular/issues/39955
335+ it ( 'should clear bootstrapped component contents when the `BrowserAnimationsModule` is imported' ,
334336 withBody ( '<div>before</div><app-root></app-root><div>after</div>' , async ( ) => {
335337 @Component ( { selector : 'app-root' , template : 'app-root content' } )
336338 class AppComponent {
@@ -355,6 +357,99 @@ describe('destroy', () => {
355357 expect ( document . body . querySelector ( 'app-root' ) ) . toBeFalsy ( ) ; // host element is removed
356358 expect ( document . body . childNodes . length ) . toEqual ( 2 ) ; // other elements are preserved
357359 } ) ) ;
360+
361+ // See https://github.com/angular/angular/issues/45108
362+ it ( 'should clear bootstrapped component contents when the animation engine is requested during initialization' ,
363+ withBody ( '<div>before</div><app-root></app-root><div>after</div>' , async ( ) => {
364+ @Injectable ( { providedIn : 'root' } )
365+ class AppService {
366+ // The `RendererFactory2` is injected here explicitly because we import the
367+ // `BrowserAnimationsModule`. The `RendererFactory2` will be provided with
368+ // `AnimationRendererFactory` which relies on the `AnimationEngine`. We want to ensure that
369+ // `ApplicationRef` is created earlier before the `AnimationEngine`, because previously the
370+ // `AnimationEngine` was created before the `ApplicationRef` (see the link above to the
371+ // original issue). The `ApplicationRef` was created after `APP_INITIALIZER` has been run
372+ // but before the root module is bootstrapped.
373+ constructor ( rendererFactory : RendererFactory2 ) { }
374+ }
375+
376+ @Component ( { selector : 'app-root' , template : 'app-root content' } )
377+ class AppComponent {
378+ }
379+
380+ @NgModule ( {
381+ imports : [ BrowserAnimationsModule ] ,
382+ declarations : [ AppComponent ] ,
383+ bootstrap : [ AppComponent ] ,
384+ providers : [
385+ // The `APP_INITIALIZER` token is requested before the root module is bootstrapped, the
386+ // `useFactory` just injects the `AppService` that injects the `RendererFactory2`.
387+ {
388+ provide : APP_INITIALIZER ,
389+ useFactory : ( ) => ( appService : AppService ) => appService ,
390+ deps : [ AppService ] ,
391+ multi : true
392+ }
393+ ]
394+ } )
395+ class AppModule {
396+ }
397+
398+ const ngModuleRef = await platformBrowserDynamic ( ) . bootstrapModule ( AppModule ) ;
399+
400+ const root = document . body . querySelector ( 'app-root' ) ! ;
401+ expect ( root . textContent ) . toEqual ( 'app-root content' ) ;
402+ expect ( document . body . childNodes . length ) . toEqual ( 3 ) ;
403+
404+ ngModuleRef . destroy ( ) ;
405+
406+ expect ( document . body . querySelector ( 'app-root' ) ) . toBeFalsy ( ) ; // host element is removed
407+ expect ( document . body . childNodes . length ) . toEqual ( 2 ) ; // other elements are preserved
408+ } ) ) ;
409+
410+ // See https://github.com/angular/angular/issues/45108
411+ it ( 'should clear standalone bootstrapped component contents when the animation engine is requested during initialization' ,
412+ withBody ( '<div>before</div><app-root></app-root><div>after</div>' , async ( ) => {
413+ @Injectable ( { providedIn : 'root' } )
414+ class AppService {
415+ // The `RendererFactory2` is injected here explicitly because we import the
416+ // `BrowserAnimationsModule`. The `RendererFactory2` will be provided with
417+ // `AnimationRendererFactory` which relies on the `AnimationEngine`. We want to ensure
418+ // that `ApplicationRef` is created earlier before the `AnimationEngine`, because
419+ // previously the `AnimationEngine` was created before the `ApplicationRef` (see the link
420+ // above to the original issue). The `ApplicationRef` was created after `APP_INITIALIZER`
421+ // has been run but before the root module is bootstrapped.
422+ constructor ( rendererFactory : RendererFactory2 ) { }
423+ }
424+
425+ @Component ( { selector : 'app-root' , template : 'app-root content' , standalone : true } )
426+ class AppComponent {
427+ }
428+
429+ const appRef = await bootstrapApplication ( AppComponent , {
430+ providers : [
431+ importProvidersFrom ( BrowserAnimationsModule ) ,
432+ // The `APP_INITIALIZER` token is requested before the standalone component is
433+ // bootstrapped, the `useFactory` just injects the `AppService` that injects the
434+ // `RendererFactory2`.
435+ {
436+ provide : APP_INITIALIZER ,
437+ useFactory : ( ) => ( appService : AppService ) => appService ,
438+ deps : [ AppService ] ,
439+ multi : true
440+ }
441+ ]
442+ } ) ;
443+
444+ const root = document . body . querySelector ( 'app-root' ) ! ;
445+ expect ( root . textContent ) . toEqual ( 'app-root content' ) ;
446+ expect ( document . body . childNodes . length ) . toEqual ( 3 ) ;
447+
448+ appRef . destroy ( ) ;
449+
450+ expect ( document . body . querySelector ( 'app-root' ) ) . toBeFalsy ( ) ; // host element is removed
451+ expect ( document . body . childNodes . length ) . toEqual ( 2 ) ; // other elements are
452+ } ) ) ;
358453} ) ;
359454} ) ( ) ;
360455
0 commit comments