DEV Community

Nhan Nguyen
Nhan Nguyen

Posted on

4

Angular Dynamic Service Instantiation Using Injector

Dynamic service instantiation can be a powerful technique for creating flexible and scalable solutions in modern Angular applications. One use case is selecting a specific implementation of a service at runtime based on user input or application state. This post will explore how to achieve this using Angular's Injector and provide a practical example of dynamically initializing payment services.

The Problem

Imagine an application that supports multiple payment methods, such as PayPal, Stripe, and Venmo. Each payment method has its own implementation but adheres to a common interface. Based on the user's selection, the appropriate service needs to be instantiated dynamically at runtime. Angular's Injector provides a seamless way to accomplish this.

Example Setup

Below is a sample implementation of dynamic service instantiation:

Service Definitions

First, define a base class PaymentBaseService and create specific services for each payment method.

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class PaymentBaseService {
  pay() {}
}

@Injectable({ providedIn: 'root' })
export class PaypalService extends PaymentBaseService {
  constructor() {
    super();
    console.log('PaypalService!');
  }

  override pay() {
    console.log('Paypal payment!');
  }
}

@Injectable({ providedIn: 'root' })
export class StripeService extends PaymentBaseService {
  constructor() {
    super();
    console.log('StripeService!');
  }

  override pay() {
    console.log('Stripe payment!');
  }
}

@Injectable({ providedIn: 'root' })
export class VenmoService extends PaymentBaseService {
  constructor() {
    super();
    console.log('VenmoService!');
  }

  override pay() {
    console.log('Venmo payment!');
  }
}
Enter fullscreen mode Exit fullscreen mode

Component Implementation

Create a component where users can select a payment method and dynamically initialize the corresponding service:

import { Component, inject, Injector } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  imports: [FormsModule],
  template: `
    <label for="payment">Choose a payment method:</label>
    <select name="payments" id='payments' [(ngModel)]="paymentMethod">
      <option value="">None</option>
      <option value="paypal">Paypal</option>
      <option value="stripe">Stripe</option>
      <option value="venmo">Venmo</option>
    </select>
    <br><br>
    <button (click)="updatePaymentService()">Submit</button>
  `,
})
export class App {
  readonly #injector = inject(Injector);

  paymentMethod: '' | 'paypal' | 'stripe' | 'venmo' = '';
  paymentService!: PaymentBaseService;

  updatePaymentService() {
    switch (this.paymentMethod) {
      case 'paypal':
        this.paymentService = this.#injector.get(PaypalService);
        break;
      case 'stripe':
        this.paymentService = this.#injector.get(StripeService);
        break;
      case 'venmo':
        this.paymentService = this.#injector.get(VenmoService);
        break;
      default:
        throw new Error(`Unknown payment type: ${this.paymentMethod}`);
    }

    this.paymentService.pay();
  }
}

bootstrapApplication(App);
Enter fullscreen mode Exit fullscreen mode

Key Points

  • Injector Usage: The Injector fetches the appropriate service dynamically. This avoids directly injecting all possible services into the component, keeping the design clean and efficient.

  • Dynamic Initialization: The updatePaymentService method determines the selected payment type and initializes the corresponding service at runtime.

  • Error Handling: The implementation includes a fallback for unknown payment types, ensuring robustness.

Benefits of Using Injector

  • Scalability: Adding a new payment method requires only defining a new service and updating the switch case in the component.

  • Flexibility: Services are instantiated only when needed, reducing memory usage.

  • Clean Design: This approach adheres to the dependency inversion principle, ensuring a loosely coupled architecture.

Conclusion

Dynamic service instantiation using Angular's Injector is a powerful feature for building flexible and scalable applications. Following the example outlined above, you can easily implement runtime-based service selection. This approach is especially useful in scenarios where the application's behavior depends on user input or dynamic configurations.

Happy coding!


I hope you found it helpful. Thanks for reading. 🙏
Let's get connected! You can find me on:

Top comments (1)

Collapse
 
hrherw333 profile image
dfh sfds

Injectors can sometimes be used to inject performance-enhancing modifications or configurations into games. For instance, some injectors allow users to alter the game's internal parameters, which can improve frame rates, reduce lag, or optimize the game for lower-spec systems. In cases where the game may have performance bottlenecks Injector 2025, injectors can be a quick and effective way to optimize settings without requiring major updates or patches from the developers.

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE