Angular Dependency Injection In Depth-Factory Providers

Factory Providers

In many applications, there are different types of users with varying privileges and access levels. For instance, let’s consider an app that has regular users and admin users. Regular users have access to basic todo management features, while admin users have access to additional advanced features. In such cases, it becomes necessary to provide different instances of the TodoServiceV2 based on the user type. This is where Factory Providers come into play.

Let’s see how we can achieve this using a Factory Provider. First, we need to modify the TodoServiceV2 to include an isAdmin property and use the @Inject decorator for the constructor parameter:

import { Inject, Injectable, InjectionToken } from "@angular/core";
import { Todo } from "../models/todo.model";

export const IS_ADMIN_TOKEN = new InjectionToken<boolean>("IS_ADMIN");
@Injectable({
  providedIn: 'root',
})
export class TodoServiceV2 {
  private todos: Todo[] = [];

  private isAdmin!: boolean;
  constructor(@Inject(IS_ADMIN_TOKEN) isAdmin: boolean) {
    this.isAdmin = isAdmin;
    console.log(`TodoServiceV2 instance created for ${this.isAdmin ? "admin" : "regular"} user.`);
  }
  getAllTodos(): Todo[] {
    return [
      { id: 1, title: 'Enhanced Todo', completed: false }
    ]
  }

  addTodo(todo: Todo): void {
    this.todos.push(todo);
  }

  updateTodo(todo: Todo): void {
    const index = this.todos.findIndex((t) => t.id === todo.id);
    if (index !== -1) {
      this.todos[index] = todo;
    }
  }

  deleteTodo(todoId: number): void {
    if (this.isAdmin) {
      const index = this.todos.findIndex((t) => t.id === todoId);
      if (index !== -1) {
        this.todos.splice(index, 1);
      }
    } else {
      throw new Error("Access denied. This function can only be executed by admin users.");
    }
  }
}


In this updated version of TodoServiceV2, we’ve added the isAdmin property and used the @Inject decorator for the constructor parameter. The @Inject decorator is used to specify the injection token for the isAdmin parameter.

Next, we need to define the Factory Provider and the necessary module. Here’s an example:

Next, let’s define the Factory Provider and the necessary module:

import { NgModule, FactoryProvider, InjectionToken } from '@angular/core';
import { TodoServiceV2 } from './todo.service-v2';

export const IS_ADMIN_TOKEN = new InjectionToken<boolean>("IS_ADMIN");

// Factory function for TodoServiceV2
export function todoServiceFactory(isAdmin: boolean): TodoServiceV2 {
  return new TodoServiceV2(isAdmin);
}

// Factory provider for TodoServiceV2
const todoServiceProvider: FactoryProvider = {
  provide: TodoServiceV2,
  useFactory: todoServiceFactory,
  deps: [IS_ADMIN_TOKEN] // Dependency injection token
};

@NgModule({
  providers: [
    todoServiceProvider,
    { provide: IS_ADMIN_TOKEN, useValue: false } // Set to true for admin user
  ]
})
export class AppModule { }

In this code snippet, we define the injection token IS_ADMIN_TOKEN as an InjectionToken<boolean> to represent the isAdmin parameter. We also define the factory function todoServiceFactory that takes the isAdmin parameter and returns a new instance of TodoServiceV2 with the provided value. The Factory Provider todoServiceProvider specifies the provide, useFactory, and deps properties, with deps indicating the dependency required by the factory function.

Finally, when injecting the TodoServiceV2 into any component or service, Angular will use the Factory Provider to create the TodoServiceV2 instance with the appropriate isAdmin value.

Here’s an example of how you can use it in a component:

import { Component, Inject, Injectable } from '@angular/core';
import { Todo } from '../models/todo.model';
import { TODO_SERVICE } from '../services/custom-injector';
import { TodoServiceV2 } from '../services/todo.service-v2';


@Component({
  selector: 'app-todo-list',
  template: `
    <ul>
      <li *ngFor="let todo of todos">
        {{ todo.title }}<button (cllick)="deleteTodo(todo.id)">Delete</button>
      </li>
    </ul>
  `,

})
export class TodoListComponent {
  todos: Todo[] = [];

  constructor(@Inject(TODO_SERVICE) private todoService: TodoServiceV2) {
    this.todos = this.todoService.getAllTodos();
  }
  deleteTodo(id: any) {
    this.todoService.deleteTodo(id);
  }
}

In this example, when the TodoListComponent is created, the TodoServiceV2 instance will be created based on the isAdmin value provided in the Factory Provider. This allows you to provide different functionality or data based on the user type.

In this example, when the TodoListComponent is created, the TodoServiceV2 instance will be created based on the isAdmin value provided in the Factory Provider. This allows you to provide different functionality or data based on the user type.

false
true
TodoListComponent
TodoServiceV2
Factory Provider
AppModule
isAdmin
Inject Token: IS_ADMIN_TOKEN
Inject Token: IS_ADMIN_TOKEN

Using a Factory Provider in this scenario gives you the flexibility to customize the creation of TodoServiceV2 instances based on dynamic conditions or user types. It enables you to provide different implementations of the service with varying behavior or features.

Next Post Previous Post
No Comment
Add Comment
comment url