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.
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.