Repository: angular
Issue: Allow lazy initialization logic with Standalone Components
Labels: feature,area: core,core: di,feature: under consideration,cross-cutting: standalone

### Which @angular/* package(s) are relevant/related to the feature request?

core

### Description

In general, some components need some logic to be initialized globally. (e.g. : Ngrx's state & effects).

This initialization logic can be added:
1. at bootstrap with `.forRoot()` pattern
2. lazily by importing a module with the `.forChild()` or `.forFeature()` pattern. _(e.g.  `EffectsModule.forFeature(...)`)_

With standalone components and without considering the NgModule compatibility function `importProvidersFrom`, we now have 2 main options for triggering initialization logic:

1. using `APP_INITIALIZER` at bootstrap,
```ts
bootstrapApplication(AppComponent, {
  providers: [
    {
      provide: APP_INITIALIZER,
      multi: true,
      useValue() {
        console.log('init...');
        inject(Store).registerEffects([...]);
      },
    },
  ],
});
```

2. using `ENVIRONMENT_INITIALIZER` in router providers.
```ts
{
  path: '...',
  providers: [
    {
      provide: ENVIRONMENT_INITIALIZER,
      multi: true,
      useValue() {
        console.log('init...');
        inject(Store).registerEffects([...]);
      },
    }
  ],
  children: [
    ...
  ],
}
```


The problem with the first approach (i.e. bootstrap) is the eager loading of all the logic (e.g. states / effects) and this doesn't scale as the main bundle will keep getting larger and larger.
The second option (`ENVIRONMENT_INITIALIZER` with router providers) allows lazy loading but is it the highest common point after the bootstrap? Not really. In fact, a state will mostly be used by components under some shared route but quickly while the app grows, one might want to reuse the component under another route or anywhere else in the app and that’s when things get tricky. As we broke the locality principle, we have to think about checking out the route providers and move the state/effects to somewhere higher… which will end up to be the bootstrap… and thus the clutter in the main bundle.
In addition to this, we can mention the challenge of debugging the state/effects provided in the routing. Someone tries to use a component outside the route and they observe the following surprising behavior “if I navigate to the feature route first, then I move to the other route, the component works, but when I land directly on this route, the action doesn’t change the state” (because the effect wasn’t loaded).

**TL;DR: providing state at bootstrap clutters the bundle and providing state at router level doesn’t scale either as one has to keep thinking about which state should be provided**

### Concrete example:
An app with an UnreadEmailComponent and bunch of other components used in /emails . Email state/effects are provided at the route level.
Months/years later, someone wants to lazy load and dynamically inject UnreadEmailComponent in a dialog or something like that when the user clicks somewhere in the landing page.
If they visit /emails first, it’s going to work but before navigating to /emails the effects/state are not initialized. First, they end up with a bug, then they need to be quite familiar with Ngrx and injectors to understand what’s happening and that’s when they  decide to move the state/effects to bootstrap which is annoying because we end up eager loading state/effects while the components that need them are lazy loaded.


### Proposed solution

### `LAZY_APP_INITIALIZER` injection token

- if provided at bootstrap, it would work like APP_INITIALIZER,
- if provided with the routing or dynamic injection, it should be called once (we shouldn’t run a function if the same reference is provided again somewhere else) when the first depending environment is created,
- if provided at node (i.e. component/directive/pipe) level, it should be called once if not already triggered during bootstrap, or env creation, or another component.

By the way, what would be the side effects of making this the default behavior of `APP_INITIALIZER`. It’s a breaking change, but what kind of apps/libs have lazy imported APP_INITIALIZER providers that are not executed and that would break the app if executed? Is that a plausible scenario?

This would allow something like this:
```ts
function provideEffects(effects) {
  return {
    provide: LAZY_APP_INITIALIZER,
    multi: true,
    useValue() {
      inject(Store).registerEffects(effects);
    }
  }
}

const emailFeatureProviders = [provideEffects([...]), provideState(...), EmailFacade];

@Component({
  standalone: true,
  template: ...,
  providers: [emailFeatureProviders]
})
export class UnreadEmail {} 

@Component({
  standalone: true,
  template: ...,
  providers: [emailFeatureProviders],
})
export class EmailInfo {} 
```

### Alternatives considered

@alxhub's side-effect trick:

```ts
function provideStoreWithFeature(feature: ...): Provider[] {
  return [
    {
      provide: Store,
      useFactory: () => {
        const rootStore = inject(RootStore);
        for (const feature of inject(STORE_FEATURE, [])) {
          rootStore.registerFeature(feature);
        }
        return rootStore;
      },
    }, {
      provide: STORE_FEATURE,
      useValue: feature,
      multi: true,
    },
  ];
}
```