@@ -10,7 +10,7 @@ import {setActiveConsumer} from '@angular/core/primitives/signals';
1010
1111import { CachedInjectorService } from '../cached_injector_service' ;
1212import { NotificationSource } from '../change_detection/scheduling/zoneless_scheduling' ;
13- import { EnvironmentInjector , InjectionToken , Injector } from '../di' ;
13+ import { EnvironmentInjector , InjectionToken , Injector , Provider } from '../di' ;
1414import { internalImportProvidersFrom } from '../di/provider_collection' ;
1515import { RuntimeError , RuntimeErrorCode } from '../errors' ;
1616import { findMatchingDehydratedView } from '../hydration/views' ;
@@ -90,7 +90,6 @@ import {
9090 setLDeferBlockDetails ,
9191 setTDeferBlockDetails ,
9292} from './utils' ;
93- import { isRouterOutletInjector } from '../render3/util/injector_utils' ;
9493
9594/**
9695 * **INTERNAL**, avoid referencing it in application code.
@@ -626,20 +625,73 @@ export function renderDeferBlockState(
626625}
627626
628627/**
629- * Creates an instance of the `OutletInjector` using a private factory
630- * function available on the `OutletInjector` class.
631- *
632- * @param parentOutletInjector Parent OutletInjector, which should be used
633- * to produce a new instance.
634- * @param parentInjector An Injector, which should be used as a parent one
635- * for a newly created `OutletInjector` instance.
628+ * Checks whether there is a cached injector associated with a given defer block
629+ * declaration and returns if it exists. If there is no cached injector present -
630+ * creates a new injector and stores in the cache.
636631 */
637- function createRouterOutletInjector (
638- parentOutletInjector : ChainedInjector ,
632+ function getOrCreateEnvironmentInjector (
639633 parentInjector : Injector ,
634+ tDetails : TDeferBlockDetails ,
635+ providers : Provider [ ] ,
640636) {
641- const outletInjector = parentOutletInjector . injector as any ;
642- return outletInjector . __ngOutletInjector ( parentInjector ) ;
637+ return parentInjector
638+ . get ( CachedInjectorService )
639+ . getOrCreateInjector (
640+ tDetails ,
641+ parentInjector as EnvironmentInjector ,
642+ providers ,
643+ ngDevMode ? 'DeferBlock Injector' : '' ,
644+ ) ;
645+ }
646+
647+ /**
648+ * Creates a new injector, which contains providers collected from dependencies (NgModules) of
649+ * defer-loaded components. This function detects different types of parent injectors and creates
650+ * a new injector based on that.
651+ */
652+ function createDeferBlockInjector (
653+ parentInjector : Injector ,
654+ tDetails : TDeferBlockDetails ,
655+ providers : Provider [ ] ,
656+ ) {
657+ // Check if the parent injector is an instance of a `ChainedInjector`.
658+ //
659+ // In this case, we retain the shape of the injector and use a newly created
660+ // `EnvironmentInjector` as a parent in the `ChainedInjector`. That is needed to
661+ // make sure that the primary injector gets consulted first (since it's typically
662+ // a NodeInjector) and `EnvironmentInjector` tree is consulted after that.
663+ if ( parentInjector instanceof ChainedInjector ) {
664+ const origInjector = parentInjector . injector ;
665+ // Guaranteed to be an environment injector
666+ const parentEnvInjector = parentInjector . parentInjector ;
667+
668+ const envInjector = getOrCreateEnvironmentInjector ( parentEnvInjector , tDetails , providers ) ;
669+ return new ChainedInjector ( origInjector , envInjector ) ;
670+ }
671+
672+ const parentEnvInjector = parentInjector . get ( EnvironmentInjector ) ;
673+
674+ // If the `parentInjector` is *not* an `EnvironmentInjector` - we need to create
675+ // a new `ChainedInjector` with the following setup:
676+ //
677+ // - the provided `parentInjector` becomes a primary injector
678+ // - an existing (real) `EnvironmentInjector` becomes a parent injector for
679+ // a newly-created one, which contains extra providers
680+ //
681+ // So the final order in which injectors would be consulted in this case would look like this:
682+ //
683+ // 1. Provided `parentInjector`
684+ // 2. Newly-created `EnvironmentInjector` with extra providers
685+ // 3. `EnvironmentInjector` from the `parentInjector`
686+ if ( parentEnvInjector !== parentInjector ) {
687+ const envInjector = getOrCreateEnvironmentInjector ( parentEnvInjector , tDetails , providers ) ;
688+ return new ChainedInjector ( parentInjector , envInjector ) ;
689+ }
690+
691+ // The `parentInjector` is an instance of an `EnvironmentInjector`.
692+ // No need for special handling, we can use `parentInjector` as a
693+ // parent injector directly.
694+ return getOrCreateEnvironmentInjector ( parentInjector , tDetails , providers ) ;
643695}
644696
645697/**
@@ -672,40 +724,12 @@ function applyDeferBlockState(
672724 // newly loaded standalone components used within the block, which may
673725 // import NgModules with providers. In order to make those providers
674726 // available for components declared in that NgModule, we create an instance
675- // of environment injector to host those providers and pass this injector
727+ // of an environment injector to host those providers and pass this injector
676728 // to the logic that creates a view.
677729 const tDetails = getTDeferBlockDetails ( hostTView , tNode ) ;
678730 const providers = tDetails . providers ;
679731 if ( providers && providers . length > 0 ) {
680- const parentInjector = hostLView [ INJECTOR ] as Injector ;
681-
682- // Note: we have a special case for Router's `OutletInjector`,
683- // since it's not an instance of the `EnvironmentInjector`, so
684- // we can't inject it. Once the `OutletInjector` is replaced
685- // with the `EnvironmentInjector` in Router's code, this special
686- // handling can be removed.
687- const isParentOutletInjector = isRouterOutletInjector ( parentInjector ) ;
688- const parentEnvInjector = isParentOutletInjector
689- ? parentInjector
690- : parentInjector . get ( EnvironmentInjector ) ;
691-
692- injector = parentEnvInjector
693- . get ( CachedInjectorService )
694- . getOrCreateInjector (
695- tDetails ,
696- parentEnvInjector as EnvironmentInjector ,
697- providers ,
698- ngDevMode ? 'DeferBlock Injector' : '' ,
699- ) ;
700-
701- // Note: this is a continuation of the special case for Router's `OutletInjector`.
702- // Since the `OutletInjector` handles `ActivatedRoute` and `ChildrenOutletContexts`
703- // dynamically (i.e. their values are not really stored statically in an injector),
704- // we need to "wrap" a defer injector into another `OutletInjector`, so we retain
705- // the dynamic resolution of the mentioned tokens.
706- if ( isParentOutletInjector ) {
707- injector = createRouterOutletInjector ( parentInjector as ChainedInjector , injector ) ;
708- }
732+ injector = createDeferBlockInjector ( hostLView [ INJECTOR ] ! , tDetails , providers ) ;
709733 }
710734 }
711735 const dehydratedView = findMatchingDehydratedView ( lContainer , activeBlockTNode . tView ! . ssrId ) ;
0 commit comments