Simplificación de los diferentes tipos de proveedores de servicios de Angular
Un proveedor de servicios de Angular entrega una versión en tiempo de ejecución de un valor de dependencia. Por lo tanto, cuando se inserta un servicio, el inyector de Angular examina los proveedores para crear la instancia del servicio.
Un proveedor de servicios de Angular entrega una versión en tiempo de ejecución de un valor de dependencia. Por lo tanto, cuando se inserta un servicio, el inyector de Angular examina los proveedores para crear la instancia del servicio.
Es el proveedor quien determina qué instancia o valor debe inyectarse en tiempo de ejecución en componentes, tuberías o directivas. Aquí hay muchas jergas implicadas, así que para entender el propósito de los tipos de proveedores, empecemos por crear un servicio. Supongamos que tenemos un servicio llamado ErrorService, que solo registra el mensaje de error.
import { Injectable } from '@angular/core';
@Injectable()
export class ErrorService {
logError(message: string) {
console.log(message);
}
}
Ahora, podemos usar este servicio en un componente, como se muestra en la lista de abajo:
import { Component } from '@angular/core';
import { ErrorService } from './errormessage.service';
@Component({
selector: 'app-root',
template: `
<input [(ngModel)]='err' type='text'/>
<button (click)='setError()'>set error</button>
`,
providers: [ErrorService]
})
export class AppComponent {
constructor(private errorservice: ErrorService) { }
err: string;
setError() {
this.errorservice.logError(this.err);
}
}
Estamos importando el servicio, pasándolo al array del proveedor e inyectándolo en el constructor del componente. Estamos llamando al método de servicio al pulsar el botón, y puedes ver el mensaje de error transmitido en la consola. Muy sencillo, ¿no?
Aquí, Angular se basará en los valores pasados en el array de proveedores de componente (o módulo) para encontrar qué instancia debe ser inyectada en tiempo de ejecución.
"Un Proveedor determina cómo se puede crear el objeto de cierto token."
Así que, cuando pasas el nombre de un servicio en el array del proveedor de componente o módulo, como se indica a continuación:
providers: [ErrorService]
Aquí, Angular va a usar el valor del token ErrorService y, para el token ErrorService, creará el objeto de la clase ErrorService. La sintaxis anterior es un atajo de la siguiente sintaxis:
providers: [{
provide: ErrorService, useClass: ErrorService
}]
La propiedad provide contiene el token que sirve como clave para
- localizando el valor de la dependencia.
- registering the dependency.
La segunda propiedad (es de cuatro tipos) se utiliza para crear el valor de dependencia. Hay cuatro posibles valores de segundo parámetro, de la siguiente manera:
- useClass
- usoExistente
- useValue
- useFactory
Acabamos de ver un ejemplo de useClass. Ahora, considera un escenario en el que tienes una nueva clase para un mejor registro de errores llamada NewErrorService.
import { Injectable } from '@angular/core';
@Injectable()
export class NewErrorService {
logError(message: string) {
console.log(message);
console.log('logged by DJ');
}
}
usoExistente
Ahora, queremos que en lugar de la instancia de ErrorService, se inyecte la instancia de NewErrorService. Además, idealmente, ambas clases deben estar implementando la misma Interfaz, lo que significa que tendrán las mismas firmas de método con diferentes implementaciones. Así que ahora, para el token ErrorService, queremos que se inyecte la instancia de NewErrorService. Se puede hacer usando useClass, como se muestra a continuación:
providers: [
NewErrorService,
{ provide: ErrorService, useClass: NewErrorService }
]
El problema con el enfoque anterior es que habrá dos instancias de NewErrorService. Esto se puede resolver usando useExisting.
providers: [
NewErrorService,
{ provide: ErrorService, useExisting: NewErrorService }
]
Ahora solo habrá una instancia de NewErrorService y para el token se creará una instancia de ErrorService de NewErrorService.
Modifiquemos el componente para usar NewErrorService.
import { Component } from '@angular/core';
import { ErrorService } from './errormessage.service';
import { NewErrorService } from './newerrormessage.service';
@Component({
selector: 'app-root',
template: `
<input [(ngModel)]='err' type='text'/>
<button (click)='setError()'>set error</button>
<button (click)='setnewError()'>Set New eroor</button>
`,
providers: [
NewErrorService,
{ provide: ErrorService, useExisting: NewErrorService }
]
})
export class AppComponent {
constructor(private errorservice: ErrorService, private newerrorservice: NewErrorService) { }
err: string;
setError() {
this.errorservice.logError(this.err);
}
setnewError() {
this.newerrorservice.logError(this.err);
}
}
Aquí, para fines demostrativos, estoy inyectando servicio en el componente, sin embargo, para usar servicio a nivel de módulo puedes inyectarlo en el propio módulo. Ahora, al hacer clic en el botón de error se llamaría a NewErrorService.
useValue
Tanto useClass como useExisting crean instancias de una clase de servicio para inyectar en un token concreto, pero a veces quieres pasar valor directamente en lugar de crear instancias. Así que, si quieres pasar un objeto ya hecho en lugar de una instancia de una clase, puedes usar useValue como se muestra en la siguiente lista:
providers: [
{
provide: ErrorService, useValue: {
logError: function (err) {
console.log('inhjected directly ' + err);
}
}
}
Así que aquí, estamos inyectando un objeto listo usando useValue. Así que para el token ErrorService, Angular inyectará el objeto.
useFactory
Podría haber un escenario en el que, hasta el momento de la ejecución, no tengas idea de qué instancia es necesaria. Necesitas crear dependencia basándote en información que no tienes hasta el último momento. Veamos como ejemplo que puede ser necesario crear una instancia de ServiceA si el usuario está conectado o ServiceB si el usuario no está conectado. La información sobre el usuario iniciado sesión puede no estar disponible hasta la última vez o puede cambiar durante el uso de la aplicación.
Necesitamos usar useFactory cuando la información sobre dependencias es dinámica. Consideremos nuestros dos servicios utilizados en ejemplos anteriores:
- ErrorMessageService
- NewErrorMessageService
Para el token ErrorService en una condición concreta queremos una instancia de ErrorMessageService o newErrorMessageService. Podemos lograrlo usando useFactory como se muestra en el listado a continuación:
providers: [
{
provide: ErrorService, useFactory: () => {
let m = 'old'; // this value can change
if (m === 'old') {
return new ErrorService();
} else {
return new NewErrorService();
}
}
}
]
Aquí, hemos tomado una condición muy codificada. Puede que tengas condiciones complejas para crear dinámicamente una instancia para un token concreto. Además, ten en cuenta que en useFactory puedes pasar la dependencia. Así que suponga que dependes de LoginService para crear instancias para el token ErrorService. Para ese pase, LoginService es deps array como se muestra en el listado a continuación:
providers: [
{
provide: ErrorService, useFactory: () => {
let m = 'old'; // this value can change
if (m === 'old') {
return new ErrorService();
} else {
return new NewErrorService();
}
},
deps: [LoginService]
}
]
Estas son cuatro formas de trabajar con los proveedores en Angular. Espero que esta publicación te resulte útil. Gracias por leer. Si te ha gustado esta publicación, compártela. Además, si no has echado un vistazo a Infragistics Ignite UI for Angular Components, ¡asegúrate de hacerlo! Tienen 30+ componentes de Angular basados en material para ayudarte a programar aplicaciones web más rápido.