Aprenda a dominar la inyección de dependencias en Angular con nuestro tutorial fácil de seguir. Descubra los beneficios de esta poderosa técnica y lleve el desarrollo de su aplicación al siguiente nivel.
Angular es un marco de código abierto para crear aplicaciones web modernas. Uno de los principios clave de Angular es la inyección de dependencia. Es un patrón de diseño que permite la creación de aplicaciones eficientes y escalables.
En esta guía completa, exploraremos qué es la inyección de dependencia en el flujo de trabajo de una empresa de desarrollo Angular, los beneficios, la implementación y las mejores prácticas. Ya sea que sea nuevo en una empresa de desarrollo de Angular o un desarrollador experimentado, esta guía lo ayudará a dominar el arte de crear aplicaciones eficientes y escalables utilizando la inyección de dependencia de Angular.
¡Profundicemos en el mundo de la inyección de dependencia!
Descripción general de la inyección de dependencia angular
¿Qué es la inyección de dependencia?
La inyección de dependencia (DI) es un patrón de diseño. La inyección de dependencia angular es un mecanismo donde se crean inyecciones de dependencia de componentes o servicios. Luego se inyecta en tiempo de ejecución en lugar de crearse dentro del servicio o componente. Esto permite la creación de código modular y débilmente acoplado. Esto significa que los componentes y servicios sólo deben preocuparse por cuáles son las dependencias, en lugar de crearlas.
Hay tres tipos de métodos de inyección de dependencia en Angular que se pueden utilizar para proporcionar dependencias a componentes y servicios. Son los siguientes.
Tipo de inyección | Descripción |
Inyección de constructores | Proporciona dependencias utilizando la función constructora de clases. |
Inyección de colocador | Se utiliza un método de establecimiento para inyectar la dependencia a través del inyector. |
Inyección de interfaz | La dependencia proporciona un método de inyector que inyecta dependencia en cualquier cliente que se le pase. El cliente también necesita tener un método de establecimiento listo para aceptar la inyección de dependencia angular. |
El uso de los métodos anteriores varía según el escenario y también depende de los requisitos de la aplicación. Veamos los beneficios de la inyección de dependencia en Angular.
Los beneficios de utilizar la inyección de dependencia en Angular
La inyección de dependencia ofrece muchos beneficios críticos e importantes para las aplicaciones Angular. Veamos algunos de ellos.
- El componente y la clase de servicio son más modulares.
- La configuración compleja y la maqueta son innecesarias; Podemos realizar pruebas de forma aislada, ya que es mucho más fácil.
- Debido a la modularidad del código, se puede reutilizar muy fácilmente.
- Codebase ahora es más fácil de administrar.
Comprender la inversión de control (IoC) y su papel en DI
En Inversión de Control (IoC), el componente o servicio no es responsable de gestionar sus dependencias. Por lo tanto, debe inyectarse en un recipiente o estructura separada. Es un principio de diseño estrechamente vinculado a la inyección de dependencia. La responsabilidad debería revertirse. Esto significa que debe inyectarse en un recipiente o marco separado.
En el siguiente código se puede ver un ejemplo de IoC y DI en Angular.
importar {Componente} desde '@angular/core'; importar {ProductService} desde './product.service'; @Componente({ selector: 'lista-de-productos-aplicaciones', URL de plantilla: './lista-de-productos.component.html', }) clase de exportación ProductListComponent { productos: cualquiera; constructor (servicio de producto privado: servicio de producto) {} ngOnInit { this.products = this.productService.getProducts; } }
En este ejemplo, ProductListComponent depende de la clase de exportación ProductServiceProductService. En lugar de crear una instancia de ProductService dentro de ProductListComponent. La función constructora se utiliza para inyectar ProductService en el componente a través del constructor.
El propio ProductService puede tener dependencias de otros servicios, pero no necesita preocuparse por crear estas dependencias porque el marco DI es responsable de crear las instancias de estos servicios e inyectarlas en ProductService.
Conceptos básicos en inyección de dependencia angular
La inyección de dependencia se ve facilitada por varios conceptos básicos. Es importante comprender estos conceptos para utilizar eficazmente la inyección de dependencia angular.
Proveedores
Los proveedores son objetos responsables de crear y administrar instancias de dependencias que se pueden inyectar en componentes y servicios. Se puede definir a nivel de componente, módulo o aplicación. Debe utilizar la propiedad Proveedor para implementarlo en Angular. Veamos un ejemplo para ver cómo se implementa.
importar {Componente, Inyectable} desde '@angular/core'; @Inyectable exportar clase MiServicio { obtener datos { devolver "Datos de MyService"; } } @Componente({ selector: 'mi-componente', proveedores: (MiServicio), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(miServicio privado: MiServicio) {} datos = this.myService.getData; }
El ejemplo anterior define una clase MyService con un decorador @Injectable. Angular debe inyectar la clase MyService como una dependencia.
propiedad useClass
También es importante especificar qué clases se utilizarán como dependencias. Veamos la propiedad useClass.
importar {Componente, Inyectable} desde '@angular/core'; @Inyectable exportar clase MiServicio { obtener datos { devolver "Datos de MyService"; } } @Inyectable exportar clase MiOtroServicio { obtener datos { devolver "Datos de MyOtherService"; } } @Componente({ selector: 'mi-componente', proveedores: ({ proporcionar: MiServicio, useClass: MiOtroServicio }), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(miServicio privado: MiServicio) {} datos = this.myService.getData; }
propiedad useValue
Esta propiedad se utiliza para especificar un valor que debe usarse como dependencia. Una implementación de ejemplo es la siguiente:
importar {Componente} desde '@angular/core'; const myValue = "Datos de useValue"; @Componente({ selector: 'mi-componente', proveedores: ({ proporcionar: 'MiValor', usarValor: miValor }), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(@Inject('MyValue') datos privados: cadena) {} }
En el ejemplo anterior, la propiedad useValue Providers especifica y proporciona un valor de cadena. El valor se inyecta en la propiedad de datos utilizando el decorador @Inject.
propiedad useFactory
Esta propiedad se usa para especificar una función de fábrica que debe usarse para crear una dependencia. Veamos un ejemplo.
importar {Componente} desde '@angular/core'; función de exportación myFactory { devolver "Datos de useFactory"; } @Componente({ selector: 'mi-componente', proveedores: ({ proporcionar: 'MyValue', useFactory: myFactory }), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(@Inject('MyValue') datos privados: cadena) {} }
El atributo useFactory se utiliza para definir y proporcionar una función de fábrica en el ejemplo anterior. El decorador @Inject indica a la aplicación que establezca la propiedad de datos en el valor devuelto por la función de fábrica.
utilizarPropiedad existente
Esta propiedad se utiliza para especificar una dependencia existente que debe usarse como valor de la nueva dependencia. Veamos un ejemplo.
importar {Componente, Inyectable} desde '@angular/core'; @Inyectable exportar clase MiServicio { obtener datos { devolver "Datos de MyService"; } } @Inyectable exportar clase MiOtroServicio { obtener datos { devolver "Datos de MyOtherService"; } } @Componente({ selector: 'mi-componente', proveedores: (MiServicio, {proporcionar: MiOtroServicio, usarExistente: MiServicio}), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(privado miOtroServicio: MiOtroServicio) {} datos = this.myOtherService.getData; }
El decorador @Injectable se utiliza para definir MyService y MyOtherService en el ejemplo anterior. Usando la propiedad useExisting, la clase MyComponent declara que MyService debe usarse como valor de MyOtherService.
Inyectores
El inyector es responsable de crear y gestionar dependencias. Una aplicación completa tiene un inyector raíz que Angular crea automáticamente. Todos los demás inyectores creados posteriormente son hijos del inyector raíz. Veamos un ejemplo.
importar {Componente, Inyectable, Inyector} desde '@angular/core'; @Inyectable exportar clase MiServicio { obtener datos { devolver "Datos de MyService"; } } @Componente({ selector: 'mi-componente', plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(inyector privado: Inyector) {} datos = this.injector.get(MyService).getData; }
En el código anterior, la clase MyService se define con un decorador @Injectable. Esto significa que la clase se puede inyectar como una dependencia. La clase MyComponent define una dependencia de Injector utilizando el constructor para inyectar una instancia de Injector. Luego, la propiedad de datos se establece en el resultado de llamar al método getData en una instancia de MyService que se recupera utilizando el método get en la instancia del inyector.
Archivos
El token de inyección de dependencia en Angular se utiliza para identificar una dependencia representada por una cadena o clase. Es importante tener en cuenta que el token de inyección se utiliza con la propiedad proporcionada para especificar qué dependencia se debe utilizar para el token proporcionado. Veamos el ejemplo de implementación de la siguiente manera:
importar {Componente, Inyectable, Inyectar} desde '@angular/core'; exportar const MY_TOKEN = 'myToken'; @Inyectable exportar clase MiServicio { obtener datos { devolver "Datos de MyService"; } } @Componente({ selector: 'mi-componente', proveedores: ({ proporcionar: MY_TOKEN, useClass: MyService }), plantilla: ' {{ data }} ' }) clase de exportación MiComponente { constructor(@Inject(MY_TOKEN) myService privado: MyService) {} datos = this.myService.getData; }
En el ejemplo de código anterior, la clase MyService le dice a Angular que esta clase se puede inyectar como una dependencia. La clase MyComponent define una dependencia de MyService especificándola como el valor de la propiedad proporcionada mediante el token MY_TOKEN. El decorador @Inject se utiliza para inyectar una instancia de MyService en la propiedad myService de la clase MyComponent. Luego, la propiedad de datos se establece en el resultado de llamar al método getData en myService.
Implementación de inyección de dependencia en Angular
Esta sección del artículo se centrará en la implementación de la inyección de dependencia angular.
Requisitos previos
Debes tener lo siguiente:
- Node.js: la última versión de Node.js en su máquina.
- Un editor de código: cualquier IDE que admita Angular.
- npm: Administrador de paquetes de nodo para instalar las dependencias necesarias.
- Angular CLI: la última versión que proporciona Angular core.
Instalar las dependencias necesarias
Si no tiene Angular preinstalado en su máquina. Utilice el siguiente comando en la terminal para instalar Angular CLI:
instalación npm -g @angular/cli
Crear un nuevo proyecto
Para crear un nuevo proyecto Angular y una aplicación de inicio, ejecute el comando CLI ng new y proporcione el nombre my-app.
ng nueva mi aplicación
Su archivo package.json debería verse así:
Tenga en cuenta que las versiones de dependencia se especifican en el archivo anterior. Estas eran las versiones al momento de crear esta guía.
Implementación
A continuación, es necesario crear un servicio para que se pueda configurar la inyección para los componentes. Los servicios son clases que proporcionan funcionalidad para toda la aplicación. Puede crear un servicio utilizando Angular CLI ejecutando el siguiente comando.
ng generar servicio mi-servicio
Una vez creado el servicio, deberá registrarlo en el sistema de inyección de dependencia de Angular. Puede hacer esto agregándolo a la matriz de proveedores en el módulo de la aplicación. El componente raíz se agrega aquí de forma predeterminada. Abra el archivo app.module.ts y agregue la siguiente línea.
importar { MyServiceService } desde './my-service.service'; @NgModule({ declaraciones: ( Componente de aplicación ), importaciones: ( módulo de navegador, Módulo de enrutamiento de aplicaciones ), proveedores: (MyServiceService), // agrega el servicio aquí arranque: (Componente de aplicación) }) exportar clase AppModule { }
Inyectar dependencias en componentes y servicios
Ahora que el servicio está configurado y registrado con el sistema de inyección de dependencia. Se puede inyectar en el componente principal o en los componentes secundarios. Para hacer esto, debe agregar un constructor al componente y especificar el servicio como parámetro. Por ejemplo, piense en un componente MyComponent que necesita utilizar el servicio MyService. Puede inyectar el servicio en el componente de esta manera. Deberías agregar esto en el archivo ts de mi componente.
importar {Componente} desde '@angular/core'; importar { MyServiceService } desde '../my-service.service'; @Componente({ selector: 'mi-componente', plantilla: '<p> {{ getMessage }} </p>' }) clase de exportación MiComponente { constructor(myService privado: MyServiceService) {} obtener mensaje: cadena { devolver this.myService.getMessage; } }
En el constructor del componente, MyServiceService se especifica como parámetro. También se define un método getMessage que recupera datos del servicio.
A continuación, en lugar de registrar un servicio con un módulo, podemos usar provideIn para registrar el servicio con el inyector raíz. Podemos hacer esto especificando provideIn: 'root' en el decorador @Injectable de nuestro servicio.
importar {Inyectable} desde '@angular/core'; @Inyectable({ proporcionado en: 'raíz' }) clase de exportación MyServiceService { obtener mensaje: cadena { return '¡Hola desde MyService!'; } }
Esto registrará el servicio con el inyector raíz. Esto lo hace disponible para cualquier componente de nuestra aplicación. También puede obtener datos de servicios de fuentes externas. Así es como registras servicios en Angular.
Inyección de dependencia en directivas y canalizaciones
Para inyectar servicios y otras dependencias en directivas y canales, debe agregar un constructor y especificar las dependencias como parámetros.
Por ejemplo, digamos que tiene una directiva personalizada que necesita utilizar el servicio MyService. Puede inyectar el servicio en la directiva de esta manera.
Primero, cree un nuevo archivo de directiva custom.directive.ts en la carpeta de la aplicación. Agregue el siguiente código.
importar {Directiva, Entrada, ElementRef} desde '@angular/core'; @Directiva({ selector: '(directiva personalizada)' }) clase de exportación CustomDirective { @Input customDirective: cadena; constructor (el privado: ElementRef) { this.customDirective=""; } ngOnInit { this.el.nativeElement.style.color = this.customDirective; } }
Esta directiva toma una entrada de tipo cadena y la usa para cambiar el color del elemento al que se aplica.
A continuación, en el archivo app.module.ts, importe CustomDirective y agréguelo a las declaraciones y matrices de exportación. Este archivo es el módulo raíz de la aplicación. En las declaraciones puedes ver todos los componentes y la directiva.
importar {NgModule} desde '@angular/core'; importar {BrowserModule} desde '@angular/platform-browser'; importar { AppComponent } desde './app.component'; importar { MyServiceService } desde './my-service.service'; importar {MiComponente} desde './mi-componente/mi-componente.componente'; importar { CustomDirective } desde './custom.directive'; @NgModule({ declaraciones: ( componente de aplicación, mi componente, CustomDirective // agregar aquí ), importaciones: ( módulo de navegador, ), proveedores: (MiServicioServicio), arranque: (Componente de aplicación) }) exportar clase AppModule { }
Finalmente, en el archivo app.component.html, aplique la directiva a un elemento y pase el color deseado. Agregue mi componente también.
<h1 customDirective="red">¡Hola mundo!</h1> <mi-componente></mi-componente>
Ejecute la aplicación
Abra una ventana de terminal y navegue hasta el directorio raíz de su proyecto. Ejecute el comando ngserve para iniciar el servidor de desarrollo.
Abra un navegador web y navegue para ver la aplicación.
Así debería verse la página web.
Inyección de dependencia jerárquica
La inyección de dependencia crea una estructura de inyectores en forma de árbol. Cada inyector de componente puede acceder al servicio proporcionado por los inyectores principal y raíz. Siempre que una clase de componente solicita un servicio, Angular lo busca en el inyector actual. Si no lo encuentra, buscará en el árbol de inyectores hasta encontrar un proveedor.
Pruebas con inyección de dependencia
TestBed es una utilidad proporcionada por Angular para realizar pruebas. Le permite configurar y crear un módulo de prueba. Puede utilizar el método TestBed.configureTestingModule para configurar el módulo de prueba con las dependencias y proveedores necesarios.
Veamos cómo se hacen las pruebas.
Probando un componente con un servicio inyectado.
importar {ComponentFixture, TestBed} desde '@angular/core/testing'; importar {MiComponente} desde './mi.componente'; importar {MiServicio} desde './mi.servicio'; describir('MiComponente', => { dejar componente: MiComponente; dejar accesorio: ComponentFixture<MiComponente>; antes de cada uno (async => { espere TestBed.configureTestingModule({ declaraciones: (MiComponente), proveedores: (MiServicio) }) .compileComponentes; }); antes de cada uno( => { accesorio = TestBed.createComponent(MiComponente); componente = accesorio.componentInstance; accesorio.detectChanges; }); it('debería crear', => { esperar(componente).toBeTruthy; }); it('debería usar servicio inyectado', => { servicio constante = TestBed.inject(MyService); spyOn(servicio, 'getData').and.returnValue('datos de prueba'); expect(component.getDataFromService).toEqual('datos de prueba'); }); });
Probando un servicio con dependencias inyectadas.
importar { TestBed } desde '@angular/core/testing'; importar {MiServicio} desde './mi.servicio'; importar {HttpClient} desde '@angular/common/http'; importar {de} desde 'rxjs'; describir('MiServicio', => { dejar servicio: MyService; dejar httpSpy: jasmine.SpyObj<HttpClient>; antes de cada uno( => { httpSpy = jasmine.createSpyObj('HttpClient', ('obtener')); TestBed.configureTestingModule({ proveedores: ( mi servicio, { proporcionar: HttpClient, useValue: httpSpy } ) }); servicio = TestBed.inject(MiServicio); }); it('debería crearse', => { esperar(servicio).toBeTruthy; }); it('debería devolver los datos esperados', => { const datos esperados = { mensaje: 'mensaje de prueba' }; httpSpy.get.and.returnValue(de(datos esperados)); servicio.getData .subscribe((datos) => { esperar(datos).toEqual(datos esperados); }); }); });
En algunos casos, es posible que desee probar un componente o servicio de forma aislada de sus dependencias. Para ello, puedes utilizar espías o servicios simulados. Una función espía registra todas las llamadas que se le realizan y luego se utiliza para validarlas en las pruebas. Un servicio simulado es una implementación de servicio falsa que le permite controlar su comportamiento y devolver valores.
Mejores prácticas y errores comunes
Es importante abordar algunas de las mejores prácticas y errores comunes al trabajar en Angular.
- Es esencial distinguir entre servicios y sus proveedores. Esto tiene el potencial de simplificar las pruebas y el mantenimiento del código.
- Cada servicio solo debe crearse una vez y compartirse en toda la aplicación, lo que puede reducir significativamente la sobrecarga y aumentar la velocidad.
- Las dependencias circulares son un error típico que se debe evitar al utilizar la inyección de dependencia. Esto puede suceder si dos o más servicios dependen entre sí de alguna manera. Como resultado, se crea un círculo vicioso de dependencia, que puede ser difícil de controlar y dar lugar a errores. Para evitar esto, es crucial examinar las interdependencias entre los servicios y organizarlas para que no haya bucles.
Si te gustó este artículo, mira:
- Dominar el enrutamiento angular: una guía completa
- Estructura de proyecto angular: mejores prácticas para archivos y carpetas
- Dominar el enlace de datos angular: una guía completa para expertos
- Principales bibliotecas y marcos de componentes de Angular UI
- ¿Qué es Angular y por qué su empresa debería considerarlo para su desarrollo?
- Los mejores frameworks Javascript de la actualidad
- Angular para empresas
- ¿Cuál es el mejor framework para el desarrollo web?
Conclusión
Comprender e implementar la inyección de dependencia en Angular es fundamental para crear aplicaciones escalables y mantenibles, ya sea que esté directamente involucrado o subcontrate el desarrollo de software de Angular. A través de esta guía, cubrimos los conceptos básicos de la inyección de dependencia angular y una implementación de ejemplo para aclarar la comprensión.
Si sigue la guía paso a paso y el ejemplo, debería comprender cómo utilizar la inyección de dependencia en sus propios proyectos de Angular o al subcontratar el desarrollo de software de Angular. Con este conocimiento, los desarrolladores pueden crear aplicaciones que sean más eficientes, flexibles y fáciles de mantener.
Fuente: BairesDev