Providers are very import part of Angular Services and Dependency Injection. Providers provides dynamic or runtime values to services. When we inject a service, the injector check the providers to create an instance of service. Providers check on runtime which instance or value should be injected to component, directives, pipes or other services.

Providers are used in component decorator with value as Array. We can also use provide to use useClass, useExisting, useValue or useFactory methods for one or more instances or to use value or function on runtime.



Providers

Lets create a service named Error using Angular CLI. This will gives better understanding of Providers in Service with example.

ng g service Error

error.service.ts

Add method printError in class with string parameter msg and print output using console.log() with parameter msg.

           <!--app.service.ts-->
import { Injectable } from '@angular/core';

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

  constructor() { }

  printError(msg:string){ console.log( msg ) }

}

Add service providers in component

Add providers in component decorator with service name in array. The token value of array is ErrorService here. Angular will create a instance of ErrorService.

In constructor of AppComponent, use private property with value ErrorService.

Add a method setError and get then value from argument err.

           <!--app.component.ts-->
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ErrorService } from './error.service';


@Component({
  selector: 'app-root',
  standalone: true,
  imports:[HomeComponent, FormsModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
  providers: [ErrorService]
  // providers: [provide: ErrorService, useClass: ErrorService]
})
export class AppComponent {
  title = 'myApp';

  constructor( private errorservice: ErrorService,){ }

  err:string="";

  setError(){
    this.errorservice.printError(this.err);
  }

} 

app.component.html

In the view layer, add an input with to way data binding. Add a button with click event and call function setError on click.

On the click of button, the input's value will be shown as error in console.

           <!--app.component.ts-->
    <h1>{{title}}</h1>
    <input type="text" [(ngModel)]="err">
    <button (click)="setError()">Click</button>

provide

The provide property in providers array of object hold token which serves as key for locating Dependency value or Dependency registration. The token can be string, type or instance of InjectionToken.

The second property is provider definition object used to create dependency value. Four possible values are

useClass Vs useExisting vs useValue vs useFactory
S NoMethodDescriptionWhere to use
1 useClass Create new instance of class Standard dependency injection for services
3 useExisting Provides an existing dependency under a new token Aliasing services, implementing specific service interfaces
2 useValue Injects a fixed value directly into the dependency Configuration settings, mock data in unit tests
4 useFactory Creates a dependency instance using a factory function Dynamic dependency creation based on runtime conditions

useClass

provide value useClass create a new instance of class when dependency is injected. The class name itself can be used as value of useClass. Also we can use an alternative class as substitute for existing class name in provider

           <!--app.component.ts-->
    import { Component } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    //import { userService } from './error.service';
    import { ErrorService } from './error.service';
    import { devErrorService } from './devError.service';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports:[HomeComponent, FormsModule],
      templateUrl: './app.component.html',
      styleUrl: './app.component.css',
      providers: [{ provide : ErrorService, useClass: devErrorService}],
      //providers: [ userService, { provide : ErrorService, useClass: devErrorService}],
    })
    export class AppComponent {
      title = 'myApp';
    
      constructor( private errorservice: ErrorService,){ }
    
      err:string="";
    
      setError(){
        this.errorservice.printError(this.err);
      }
    
    } 
    

useExisting

useExisting value use alias of existing dependency. Instead of creating a new instance, angular will use existing instance registered under different token. Instead of two instances, there will be one instance only.

           <!--app.component.ts-->
    import { Component } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { ErrorService } from './error.service';
    import { devErrorService } from './devError.service';
    
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports:[HomeComponent, FormsModule],
      templateUrl: './app.component.html',
      styleUrl: './app.component.css',
      providers: [{ provide : ErrorService, useExisting: devErrorService}]
    })
    export class AppComponent {
      title = 'myApp';
    
      constructor( private errorservice: ErrorService,){ }
    
      err:string="";
    
      setError(){
        this.errorservice.printError(this.err);
      }
    
    } 
    

useValue

useValue pass object value instead of creating new instance. This is recommended when an existing value is already available as object or function.

           <!--app.component.ts-->
    import { Component } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { ErrorService } from './error.service';
    
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports:[HomeComponent, FormsModule],
      templateUrl: './app.component.html',
      styleUrl: './app.component.css',
      providers: [{ provide : ErrorService, useValue:{ error: function(err){ console.log(`Injected by ${err}`)}}}]
      //providers: [{ provide : ErrorService, useValue:{ a:"a", b: "b" }}]
    })
    export class AppComponent {
      title = 'myApp';
    
      constructor( private errorservice: ErrorService){
        console.log( errorservice );                        // return { a:"a", b: "b" }
      }
     
    } 
    

useFactory

useFactory is recommended when the value is dynamic. For example the instance of either errorservice or devErrorService will be used on runtime.

           <!--app.component.ts-->
    import { Component } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { ErrorService } from './error.service';
    import { LoginService } from './login.service';
    
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports:[HomeComponent, FormsModule],
      templateUrl: './app.component.html',
      styleUrl: './app.component.css',
      providers: [{ provide : ErrorService, useFactory: ()=>{
          let x=true;
          if(x){ return new ErrorService() }
          else{ return new devErrorService() }
      }}],
      deps:[LoginService]
    })
    export class AppComponent {
      title = 'myApp';
    
      constructor( private errorservice: ErrorService){ }
    
    
    }