Explicación de los servicios angulares y la inyección de dependencia

Servicios e inyectores

Los componentes son responsables de los datos que se procesan en la plantilla. Tener servicios externos a los que recurrir puede simplificar esta responsabilidad. Además, encapsular elementos extraños es mucho más fácil de mantener.

Delegar demasiadas responsabilidades en un solo componente puede complicar la clase de componente. ¿Y si estas responsabilidades se aplicaran a varios componentes? Copiar y pegar dicha lógica es una práctica extremadamente deficiente. Cualquier cambio futuro en la lógica sería más difícil de implementar y probar.

Angular pretendía frenar este problema con servicios e inyección de dependencia. Ambos conceptos funcionan juntos para proporcionar una funcionalidad modular .

Los componentes tampoco necesitan proporcionar ninguna información extraña. Un servicio importa lo que necesita para funcionar en nombre de los componentes a los que da servicio . Los componentes solo necesitan instanciar el servicio. Desde allí servicio de sus propias necesidades con la instancia de servicio instanciada.

En cuanto a las pruebas y las modificaciones futuras, toda la lógica está en un solo lugar. El servicio crea una instancia desde su origen. Las pruebas y modificaciones a la fuente se aplican en cualquier lugar donde se inyecte el servicio.

Introducción a los servicios

Un servicio es un tipo de esquema disponible en Angular. Es generable por la interfaz de línea de comandos (CLI): ng generate service [name-of-service]. Reemplace [name-of-service]con un nombre preferible. El comando CLI produce lo siguiente.

import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class LoggerService { constructor() { } }

La lógica de un servicio es distinta dentro de su clase. Angular interpreta una clase como un servicio inyectable basado en el @Injectabledecorador. Los servicios de inyectables deben registrarse con un inyector.

El componente crea una instancia de un servicio mientras que el inyector proporciona esa instancia. Siga leyendo en la siguiente sección para obtener más información sobre los inyectores.

El @Injectablecampo de metadatos providedIn: ‘root’apunta al módulo raíz de la aplicación actual ( app.module.ts). Registra el servicio con el inyector del módulo para que pueda inyectar ese servicio en cualquiera de sus hijos.

Los inyectores son los componentes básicos del sistema de inyección de dependencias de Angular. Los inyectores son un buen lugar para enfocar su atención antes de continuar con los servicios.

Inyectores

Una aplicación, comenzando por app.module.ts, contiene una jerarquía de inyectores. Existen junto a cada módulo y componente en el árbol de la aplicación.

Jerarquía de aplicaciones

Los círculos verdes indican inyectores. Proporcionan instancias de servicio para crear instancias de componentes. Según el inyector con el que esté registrado un servicio, puede que esté disponible o no para un componente.

Los servicios registrados en la raíz de la aplicación ( app.module.ts) están disponibles para todos los componentes. Es posible que un inyector de un componente no tenga un determinado servicio registrado. Si ese es el caso y el componente solicita su instanciación, el inyector lo remitirá a su padre. Esta tendencia continúa hasta que se llega al inyector raíz o se encuentra el servicio.

Mirando el diagrama, digamos que un servicio se registra en el inyector del punto B. Todos los componentes en el punto C y hacia abajo no podrán acceder al servicio registrado en el inyector de B. Los inyectores nunca cederán a sus hijos por una instancia de servicio.

Inyección de dependencia

Hay varias formas de registrar un servicio con los inyectores de una aplicación.

El providedIn: ‘root’campo de metadatos de @Injectableproporciona el enfoque más recomendado. Este campo de metadatos se lanzó con Angular 6.

Como se mencionó anteriormente, providedIn: ‘root’registra un servicio con el inyector del módulo raíz. Como resultado, se puede crear una instancia en toda la aplicación.

La novedad de providedIn: ‘root’es hacer temblar los árboles . Si el servicio no se utiliza a pesar de su registro, la aplicación lo sacude en tiempo de ejecución. De esa forma no consume ningún recurso.

Las otras dos formas son más directas y tradicionales. Por supuesto, no ofrecen sacudidas de árboles.

Un servicio puede registrarse con cualquier inyector a lo largo del árbol de componentes. Insertar el servicio como proveedor en el @Componentcampo de metadatos: providers: []. El servicio está disponible para el componente y sus hijos.

En la tercera estrategia de registro, los providers: []metadatos existen como su propio campo en el @NgModuledecorador. Se puede crear una instancia del servicio desde el módulo hasta el árbol de componentes subyacente.

Recuerde que a diferencia de lo que ocurre con el registro providedIn: ‘root’, el @NgModuleregistro no ofrece la posibilidad de sacudir árboles. Por lo demás, ambas estrategias son idénticas. Una vez que un servicio se registra @NgModule, consume recursos incluso si la aplicación no lo utiliza.

Servicios continuados

Lo siguiente es escribir un servicio real. En resumen, los servicios manejan ciertas funciones en nombre de los componentes de una aplicación.

Los servicios se destacan en el manejo de operaciones comunes. Al hacerlo, ahorran a los componentes la responsabilidad. Ahorra tiempo al no tener que volver a escribir operaciones comunes en varios componentes. También es más comprobable porque el código está en un solo lugar. Los cambios solo deben ocurrir en un lugar sin tener que buscar en otro lugar.

Casos de uso

Un par de ejemplos contribuyen en gran medida a una comprensión completa de los servicios.

  • registros de la consola
  • Solicitudes de API

Ambos son comunes en la mayoría de las aplicaciones. Tener servicios para manejar estas operaciones reducirá la complejidad de los componentes.

Registros de la consola

Este ejemplo se construye a partir del @Injectableesqueleto base . El esqueleto está disponible mediante la ejecución de CLI ( ng generate service [name-of-service]]).

// services/logger.service.ts import { Injectable } from '@angular/core'; interface LogMessage { message:string; timestamp:Date; } @Injectable({ providedIn: 'root' }) export class LoggerService { callStack:LogMessage[] = []; constructor() { } addLog(message:string):void { // prepend new log to bottom of stack this.callStack = [{ message, timestamp: new Date() }].concat(this.callStack); } clear():void { // clear stack this.callStack = []; } printHead():void  printLog():void { // print bottom to top of stack on screen this.callStack.reverse().forEach((logMessage) => console.log(logMessage)); } getLog():LogMessage[] { // return the entire log as an array return this.callStack.reverse(); } }

LoggerService se registra con el módulo raíz a través de los @Injectablemetadatos. Por lo tanto, puede crear una instancia en el app.component.html.

// app.component.ts import { Component, OnInit } from '@angular/core'; import { LoggerService } from './services/logger.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { logs:object[] = []; constructor(private logger:LoggerService) { } updateLog():void { this.logger.printHead(); this.logs = this.logger.getLog(); } logMessage(event:any, message:string):void { event.preventDefault(); this.logger.addLog(`Message: ${message}`); this.updateLog(); } clearLog():void { this.logger.clear(); this.logs = []; } ngOnInit():void { this.logger.addLog(“View Initialized”); this.updateLog(); } }

La plantilla HTML proporciona más información sobre el uso de LoggerService por parte del componente.

Log Example

SUBMIT

Complete Log

CLEAR
  • {{ logs.length - i }} > {{ log.message }} @ {{ log.timestamp }}

Esto tiene la sensación de una aplicación ToDo. Puede registrar mensajes y borrar el registro de mensajes. ¡Imagínese si toda la lógica del servicio se introdujera en AppComponent! Habría complicado el código. LoggerService mantiene el código relacionado con el registro encapsulado de la clase AppComponent principal.

Obtener solicitudes

Here is one more example worth playing around with. This example is possible thanks to typicode’s JSONPlaceholder1. The API is public and free to use.

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; // //jsonplaceholder.typicode.com // public API created by typicode @ //github.com/typicode interface Post { userId:number; id:number; title:string; body:string; } @Injectable({ providedIn: 'root' }) export class PlaceholderService { constructor(private http:HttpClient) { } getPosts():Observable { return this.http.get('//jsonplaceholder.typicode.com/posts'); } getPost(id:number):Observable { return this.http.get(`//jsonplaceholder.typicode.com/posts/${id}`); } }

This is more of a stand-alone piece than a fully fleshed out example. Fetch requests tend to work better as an injectable service. The alternative is an over-complicated component. The injected class subscribes to what the PlaceholderService pre-configures.

Conclusion

Services and dependency injection are very useful together. They allow developers to encapsulate common logic and inject across multiple different components. This alone is a massive convenience for any future maintenance.

Injectors work as intermediaries. They mediate between instantiating components and a reservoir of registered services. Injectors offer these instantiable services to their branch children.

Consulte los siguientes enlaces para obtener más información sobre los servicios y la inyección de dependencias.

Recursos para Angular

  • Documentación angular
  • En introducción a la inyección de dependencia angular
  • ¿Qué es la inyección de dependencia y cuándo usarla?
  • Los mejores ejemplos de código angular
  • Repositorio angular de GitHub
  • Inyección de dependencia
  • Introducción a los servicios y DI