You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular依赖注入中useFactory()的判断上下文来源及示例问询

Understanding the Context for useFactory() Conditionals in Angular

Great question—this is a common point of confusion when working with Angular's dependency injection system! The key thing to remember is that useFactory() runs during the DI container initialization phase, not when the component is created. That means your conditional logic can't rely on component state (since components don't exist yet), but it can access a range of DI-aware or static context sources. Let's walk through the most common ones with concrete examples.

1. Static Environment Variables

Angular's built-in environment files are perfect for static configuration flags that don't change at runtime. You can reference these directly in your factory function.

Example:

First, define a flag in your environment.ts:

// environment.ts
export const environment = {
  production: false,
  useMockData: true // Flag to toggle mock vs real service
};

Then use it in your module's provider configuration:

import { NgModule } from '@angular/core';
import { environment } from '../environments/environment';
import { DataService } from './data.service';
import { MockDataService } from './mock-data.service';

@NgModule({
  providers: [
    {
      provide: DataService,
      useFactory: () => {
        // Context comes from static environment variable
        if (environment.useMockData) {
          return new MockDataService();
        } else {
          return new DataService();
        }
      }
    }
  ]
})
export class AppModule {}

2. Injected Dependencies (Pre-Initialized Services)

You can inject other services that are already available in the DI container into your factory function. This is super useful for runtime-loaded configuration (like settings fetched from an API).

Example: Using a Config Service

First, create a service to hold your runtime config:

// config.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ConfigService {
  apiConfig: { useLegacyApi: boolean } | null = null;

  loadConfig(): Promise<void> {
    // Simulate fetching config from API
    return fetch('/api/config')
      .then(res => res.json())
      .then(config => {
        this.apiConfig = config;
      });
  }
}

Next, use APP_INITIALIZER to load the config before the app initializes:

// app.module.ts
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { ConfigService } from './config.service';
import { LegacyApiService } from './legacy-api.service';
import { ModernApiService } from './modern-api.service';
import { ApiService } from './api.service';

// Factory to load config on app start
export function initConfig(configService: ConfigService) {
  return () => configService.loadConfig();
}

@NgModule({
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initConfig,
      deps: [ConfigService],
      multi: true
    },
    {
      provide: ApiService,
      useFactory: (configService: ConfigService) => {
        // Context comes from the injected ConfigService (pre-loaded)
        if (configService.apiConfig?.useLegacyApi) {
          return new LegacyApiService();
        } else {
          return new ModernApiService();
        }
      },
      deps: [ConfigService] // Declare dependencies for the factory
    }
  ]
})
export class AppModule {}

3. Platform Information

You can check whether the app is running in the browser or on the server (for SSR) using Angular's PLATFORM_ID token.

Example:

import { NgModule, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { StorageService } from './storage.service';
import { ServerStorageService } from './server-storage.service';

@NgModule({
  providers: [
    {
      provide: StorageService,
      useFactory: (platformId: object) => {
        // Context comes from the PLATFORM_ID token
        if (isPlatformBrowser(platformId)) {
          return new StorageService(); // Uses localStorage
        } else if (isPlatformServer(platformId)) {
          return new ServerStorageService(); // Uses server-side storage
        }
        throw new Error('Unsupported platform');
      },
      deps: [PLATFORM_ID]
    }
  ]
})
export class CoreModule {}

4. Global Constants/Static Configuration Objects

You can also define static configuration objects outside of Angular's DI system and reference them directly in your factory.

Example:

// app-config.ts
export const AppConfig = {
  features: {
    enableNewDashboard: true
  }
};

// dashboard.module.ts
import { NgModule } from '@angular/core';
import { AppConfig } from '../app-config';
import { NewDashboardComponent } from './new-dashboard.component';
import { LegacyDashboardComponent } from './legacy-dashboard.component';
import { DashboardComponent } from './dashboard.component';

@NgModule({
  providers: [
    {
      provide: DashboardComponent,
      useFactory: () => {
        // Context comes from a static global config
        if (AppConfig.features.enableNewDashboard) {
          return new NewDashboardComponent();
        } else {
          return new LegacyDashboardComponent();
        }
      }
    }
  ]
})
export class DashboardModule {}

Key Takeaway

All these context sources are available before components are created because they're either:

  • Static (environment vars, global constants)
  • Initialized early via APP_INITIALIZER
  • Provided by Angular's core DI system (like PLATFORM_ID)

This lets you make dependency decisions at the DI level, ensuring the right service is injected wherever it's needed—without relying on component state.

内容的提问来源于stack exchange,提问作者Ole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:20:28