Provider Configuration

Back

Loading concept...

Dependency Injection: Provider Configuration

The Restaurant Kitchen Analogy

Imagine you own a restaurant. Every day, your chefs need ingredients to cook meals. But here’s the question: Where do the ingredients come from?

You could:

  1. Hire a specific supplier (useClass)
  2. Use pre-made ingredients from your pantry (useValue)
  3. Have a special recipe to create ingredients on-demand (useFactory)
  4. Create a secret code name for special items (InjectionToken)

This is exactly what Provider Configuration does in Angular! It tells Angular: “When someone asks for THIS thing, give them THAT thing.”


What is a Provider?

A provider is like a recipe card that tells Angular:

“When someone needs IngredientService, here’s how to get it!”

graph TD A["Component asks for Service"] --> B{Angular checks Providers} B --> C["Provider says how to create it"] C --> D["Service is delivered!"]

Think of providers as instructions for Angular’s delivery system.


useClass Provider

The Story

Your restaurant needs a Chef. You write a job posting:

“I need a Chef. Hire someone from the ProfessionalChef class.”

That’s useClass! You’re saying: “When someone asks for Chef, create a new ProfessionalChef.”

Simple Example

// The service we want
@Injectable()
class LoggerService {
  log(msg: string) {
    console.log(msg);
  }
}

// Provider configuration
providers: [
  {
    provide: LoggerService,
    useClass: LoggerService
  }
]

Why Use It?

Swap implementations easily!

// Development: use a noisy logger
// Production: use a quiet logger

providers: [
  {
    provide: LoggerService,
    useClass: environment.production
      ? QuietLogger
      : NoisyLogger
  }
]

Real-Life Scenario

Imagine you have a DataService. In testing, you want a MockDataService:

// Normal app
providers: [
  { provide: DataService, useClass: DataService }
]

// Testing
providers: [
  { provide: DataService, useClass: MockDataService }
]

Your component asks for DataService. Angular gives it MockDataService. Magic!


useValue Provider

The Story

Instead of hiring a chef, you have a pre-made sandwich sitting in the fridge. When someone asks for lunch, you just hand them the sandwich.

No cooking. No waiting. Just grab and give.

Simple Example

// A simple configuration object
const APP_CONFIG = {
  apiUrl: 'https://api.example.com',
  maxRetries: 3,
  timeout: 5000
};

// Provider: just use this value!
providers: [
  {
    provide: 'APP_CONFIG',
    useValue: APP_CONFIG
  }
]

When to Use It?

Perfect for:

  • Configuration objects
  • Constants
  • Simple values that don’t need to be “created”

Real-Life Scenario

// API settings
const API_SETTINGS = {
  baseUrl: 'https://myapp.com/api',
  version: 'v2'
};

// Provide it
providers: [
  { provide: 'API_SETTINGS', useValue: API_SETTINGS }
]

// Use it in a service
constructor(
  @Inject('API_SETTINGS') private settings: any
) {
  console.log(this.settings.baseUrl);
  // Output: https://myapp.com/api
}

useFactory Provider

The Story

You don’t have a fixed chef or a pre-made sandwich. Instead, you have a recipe that decides what to make based on the situation.

Is it morning? Make breakfast. Is it evening? Make dinner.

useFactory is your decision-making recipe!

Simple Example

// Factory function - makes decisions!
function loggerFactory(isDev: boolean) {
  if (isDev) {
    return new VerboseLogger();
  }
  return new SimpleLogger();
}

// Provider with factory
providers: [
  {
    provide: LoggerService,
    useFactory: loggerFactory,
    deps: ['IS_DEV_MODE']
  }
]

The Power of Factories

Factories can:

  • Make decisions based on conditions
  • Depend on other services
  • Run async operations (with a bit more setup)

Real-Life Scenario

// Factory that needs another service
function dataServiceFactory(
  http: HttpClient,
  config: ConfigService
) {
  const baseUrl = config.getApiUrl();
  return new DataService(http, baseUrl);
}

// Provider
providers: [
  {
    provide: DataService,
    useFactory: dataServiceFactory,
    deps: [HttpClient, ConfigService]
  }
]

Understanding deps

The deps array tells Angular: “Before running the factory, get me THESE things.”

graph TD A["Angular sees Factory Provider"] --> B["Reads deps array"] B --> C["Gets HttpClient"] B --> D["Gets ConfigService"] C --> E["Calls factory with dependencies"] D --> E E --> F["Factory returns DataService"]

InjectionToken

The Story

In your restaurant, you have special items without official names. Maybe it’s grandma’s secret sauce or the special of the day.

You create a secret code: “ITEM-47” means grandma’s sauce.

InjectionToken is that secret code for Angular!

The Problem It Solves

// This causes problems! Strings can collide.
providers: [
  { provide: 'config', useValue: {...} }
]

// Another library also uses 'config'!
// CONFLICT!

The Solution: InjectionToken

// Create a unique token
export const APP_CONFIG = new InjectionToken<AppConfig>(
  'app.config'  // Description for debugging
);

// Use the token
providers: [
  {
    provide: APP_CONFIG,
    useValue: { apiUrl: 'https://...' }
  }
]

// Inject using the token
constructor(
  @Inject(APP_CONFIG) private config: AppConfig
) { }

Why Is It Unique?

Each InjectionToken is a unique object in memory. Even if two tokens have the same description, they’re different!

const TOKEN_A = new InjectionToken('config');
const TOKEN_B = new InjectionToken('config');

// TOKEN_A !== TOKEN_B (they're different!)

Real-Life Scenario

// tokens.ts
export const API_URL = new InjectionToken<string>('api.url');
export const MAX_RETRIES = new InjectionToken<number>('max.retries');

// app.module.ts
providers: [
  { provide: API_URL, useValue: 'https://api.myapp.com' },
  { provide: MAX_RETRIES, useValue: 3 }
]

// my.service.ts
constructor(
  @Inject(API_URL) private apiUrl: string,
  @Inject(MAX_RETRIES) private maxRetries: number
) { }

All Four Together: A Complete Picture

Let’s see all four in action at our restaurant:

// 1. InjectionToken - Create secret codes
export const KITCHEN_CONFIG = new InjectionToken<KitchenConfig>(
  'kitchen.config'
);

// 2. useValue - The pre-made pantry items
const kitchenSettings = {
  maxDishes: 100,
  style: 'Italian'
};

// 3. useClass - Hire the chef class
@Injectable()
class ItalianChef implements Chef { }

// 4. useFactory - Decision-making recipe
function menuFactory(config: KitchenConfig) {
  if (config.style === 'Italian') {
    return new ItalianMenu();
  }
  return new GenericMenu();
}

// All providers together
providers: [
  // useValue
  { provide: KITCHEN_CONFIG, useValue: kitchenSettings },

  // useClass
  { provide: Chef, useClass: ItalianChef },

  // useFactory with deps
  {
    provide: Menu,
    useFactory: menuFactory,
    deps: [KITCHEN_CONFIG]
  }
]

Quick Decision Guide

I need to… Use this
Create instance of a class useClass
Use a ready-made value/object useValue
Make runtime decisions useFactory
Avoid string collisions InjectionToken

Common Patterns

Pattern 1: Environment-Based Switching

providers: [
  {
    provide: ApiService,
    useClass: environment.production
      ? ProductionApi
      : MockApi
  }
]

Pattern 2: Configuration Token

// Define token with default value
export const TIMEOUT = new InjectionToken<number>(
  'timeout',
  { factory: () => 3000 }  // Default!
);

Pattern 3: Factory with Multiple Deps

{
  provide: ComplexService,
  useFactory: (a, b, c) => new ComplexService(a, b, c),
  deps: [ServiceA, ServiceB, ServiceC]
}

You Did It!

You now understand the four ways to configure providers in Angular:

  1. useClass - “Create an instance of THIS class”
  2. useValue - “Use THIS exact value”
  3. useFactory - “Run THIS function to decide”
  4. InjectionToken - “Here’s a unique name tag”

Think of Angular’s DI as a smart delivery system. Providers are the delivery instructions. And you’re now the master delivery planner!

graph TD A["🏠 Component needs something"] --> B{📋 Check Provider} B -->|useClass| C["🏭 Create from Class"] B -->|useValue| D["📦 Grab the Value"] B -->|useFactory| E["🔧 Run Factory Function"] C --> F["✅ Delivered!"] D --> F E --> F

Go build something amazing!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.