No desenvolvimento de software, existem vários padrões propostos para lidar com a injeção de dependência. Angular impõe o padrão de injeção do construtor, que usa o construtor para passar as dependências de uma classe como parâmetros do construtor.
O Angular tem sua própria estrutura integrada de injeção de dependência (DI) que fornece dependências para classes na instanciação. Este é um recurso importante para construir aplicativos da web escalonáveis no Angular.
Neste tutorial, mostraremos como a injeção de dependência funciona no Angular, percorrendo alguns exemplos práticos. Também revisaremos algumas práticas recomendadas e abordaremos algumas abordagens diferentes para lidar com a injeção de dependência em um aplicativo Angular.
Para acompanhar, você deve ter o seguinte:
- Node.js V10.x
- Conhecimento prévio de trabalho do Angular
- Conhecimento prévio de trabalho em TypeScript
O que é injeção de dependência no Angular?
De acordo com a documentação oficial do Angular , injeção de dependência é “um padrão de design no qual uma classe solicita dependências de fontes externas, em vez de do que criá-los. ”
Resumindo, a injeção de dependência angular visa separar a implementação de serviços dos componentes. Isso facilita o teste, a substituição e a alteração dos serviços sem afetar os componentes dependentes desses serviços.
Na maioria das vezes, você encontrará alguns tutoriais ou bases de código angulares que tratam da injeção de dependência desta maneira:
ng gerar produtos/produtos de serviço
O comando acima cria um novo serviço Angular junto com outros arquivos que o acompanham.
//app/products/product.model.ts interface de exportação do produto { número de identidade; nome: string; }
O snippet acima usa a interface TypeScript para criar um modelo para validar os dados retornados do serviço de produtos.
//app/products/product.service.ts importar {injetável} de'@ angular/core'; importar {Product} de'./product.model'; @Injectable ({ fornecido em:'root' }) export class ProductService { construtor () {} getProducts (): Produto [] { Retorna [ {id: 1, nome:'Nike'}, {id: 2, nome:'Balenciaga'}, {id: 3, nome:'Gucci'}, {id: 4, nome:'Addidas'}, ]; } }
A propriedade providedIn
cria um provedor para o serviço. Nesse caso, providedIn:'root'
especifica que o Angular deve fornecer o serviço no injetor raiz (ou seja, disponibilizá-lo em todo o aplicativo).
Agora, ProductService
pode ser injetado em qualquer lugar em nosso aplicativo:
//app/products/product-list/product-list.component.ts import {Component, OnInit} de'@ angular/core'; importar {Product} de'../product.model'; importar {ProductService} de'../product.service'; @Componente({ seletor:'app-product-list', templateUrl:'./product-list.component.html', styleUrls: ['./product-list.component.css'], provedores: [ProductService] }) export class ProductListComponent implementa OnInit { produtos: Produto []; productService privado: ProductService; construtor () { this.productService=new ProductService (); } ngOnInit (): void { this.products=this.productService.getProducts (); } }
O snippet acima instancia a propriedade privada productService
usando a nova palavra-chave no construtor do componente, chama o método getProducts
de productService
dentro do ngOnInit
e atribui o valor de retorno à propriedade products
.
//app/products/product-list/product-list.component.htmlNossos produtos
-
(S/N: {{product.id}}) {{product.name}}
O snippet acima usa a diretiva ngFor
para exibir a lista de produtos.
Se você executar o aplicativo usando o comando ng serve
, tudo deve funcionar bem.
Embora tenhamos dissociado com sucesso nosso componente da lógica do produto por meio do serviço Angular, que é o principal objetivo do DI, essa abordagem ainda tem duas desvantagens principais. A primeira é que um novo serviço é criado cada vez que o ProductListComponent
é renderizado. Isso pode impactar negativamente o desempenho do aplicativo em uma situação em que um serviço singleton é esperado.
Em segundo lugar, se alterarmos o construtor de ProductService
para acomodar outra dependência, também precisaremos alterar a implementação do construtor ProductListComponent
. Isso significa que o componente ainda está fortemente acoplado à implementação do serviço, o que pode tornar o teste do serviço muito difícil.
A prática recomendada para lidar com a injeção de dependência no Angular é a seguinte.
Atualize product-list.component.ts
conforme mostrado abaixo:
//app/products/product-list/product-list.component.ts ... export class ProductListComponent implementa OnInit { produtos: Produto []; construtor (productService privado: ProductService) {} ngOnInit (): void { this.products=this.productService.getProducts (); }
Dessa forma, o componente não precisa saber como instanciar o serviço. Em vez disso, ele recebe a dependência e a injeta por meio de seu construtor. Essa abordagem torna mais fácil testar o serviço.
Como lidar com a injeção de dependência no Angular
Ao lidar com a injeção de dependência em um aplicativo Angular, você pode adotar uma abordagem baseada em aplicativo ou em componente. Vamos examinar as diferenças.
Injeção de dependência baseada em aplicativo
O framework Angular DI torna as dependências disponíveis em todo o aplicativo, fornecendo um injetor que mantém uma lista de todas as dependências do necessidades do aplicativo. Quando um componente ou serviço deseja usar uma dependência, o injetor primeiro verifica se já criou uma instância dessa dependência. Caso contrário, ele cria uma nova, a retorna ao componente e reserva uma cópia para uso posterior, de modo que da próxima vez que a mesma dependência for solicitada, ele retorne a dependência reservada em vez de criar uma nova.
Existem hierarquias associadas a injetores em um aplicativo Angular. Sempre que um componente Angular define um token em seu construtor, o injetor procura um tipo que corresponda a esse token no pool de provedores registrados. Se nenhuma correspondência for encontrada, ele delega a pesquisa no provedor do componente pai por meio da árvore injetora de componente. Se encontrar a dependência, ele para e retorna uma instância dela para o componente que a solicitou.
Se a pesquisa do provedor terminar sem correspondência, ele retorna ao injetor do componente que solicitou o provedor e pesquisa através dos injetores de todos os módulos pai na hierarquia do injetor do módulo até atingir o injetor raiz. Se nenhuma correspondência for encontrada, o Angular lançará uma exceção. Caso contrário, ele retorna uma instância da dependência do componente.
Já examinamos alguns trechos de código práticos para essa abordagem. Sinta-se à vontade para consultar a seção anterior se desejar revisá-la.
Injeção de dependência baseada em componente
Esta abordagem é conhecida por injetar as dependências diretamente na árvore de componentes usando a propriedade @Component
do decorador Provedores
para registrar serviços com um injetor de componente. Essa abordagem é comumente usada em aplicativos angulares.
Ao compartilhar dependências entre componentes filhos, as dependências são compartilhadas entre todos os componentes filhos do componente que fornece as dependências. Eles estão prontamente disponíveis para injeção nos construtores dos componentes filhos, fazendo com que cada componente filho reutilize a mesma instância do serviço do componente pai.
Digamos que desejemos exibir uma lista de produtos adicionados recentemente. Obviamente, o modelo para exibir uma lista de produtos adicionados recentemente é o mesmo para exibir todos os produtos. Portanto, podemos compartilhar a dependência (serviço) de produtos
entre os componentes ProductListComponent
e RecentProductComponent
.
Crie um novo componente chamado recent-products
dentro do módulo products
com o seguinte comando:
ng gerar produtos de componentes/produtos recentes--module=produtos
Atualize recent-products.component.ts
da seguinte maneira:
import {Component, OnInit} de'@ angular/core'; importar {ProductService} de'../product.service'; importar {Product} de'../product.model'; @Componente({ seletor:'app-recent-products', templateUrl:'./recent-products.component.html', styleUrls: ['./recent-products.component.css'] }) export class RecentProductsComponent implementa OnInit { produtos: Produto []; construtor (productService privado: ProductService) {} ngOnInit (): void { this.products=this.productService.getHeroes (); } }
Aqui, injetamos ProductService
no construtor de RecentProductsComponent
sem realmente fornecê-lo por meio da propriedade provider
do @component
decorator, como fizemos para ProductListComponent
.
Como consideramos a propriedade provider
ausente do decorador @component
? Sem isso, o RecentProductsComponent
não saberá como criar uma instância de ProductService
.
Atualize o modelo ProductListComponent
da seguinte maneira:
//app/products/product-list/product-list.component.htmlNossos produtos
-
(S/N: {{product.id}}) {{product.name}}
Responderemos à pergunta anterior tornando o RecentProductComponent
um filho direto do ProductListComponent
, dando ao RecentProductComponent
acesso a todas as dependências fornecidas por ProductListComponent
.
Atualize recent-products.component.html
da seguinte maneira:
//app/products/recent-products/recent-products.component.htmlProdutos recentes
- {{Nome do Produto}}
Aqui, aplicamos o tubo de fatia à instrução ngFor
para exibir apenas os primeiros cinco produtos.
Ao executar o aplicativo com o comando ng serve
, você deve ver uma lista de todos os produtos e produtos recentes renderizados no navegador.
Conclusão
Neste tutorial, estabelecemos uma compreensão básica da injeção de dependência angular. Vimos vários exemplos práticos para demonstrar como as dependências são compartilhadas entre os componentes filhos, bem como o aplicativo inteiro. Também revisamos algumas práticas recomendadas para implementar injeção de dependência em seu próximo aplicativo Angular.
A postagem Como funciona a injeção de dependência no Angular apareceu primeiro no LogRocket Blog .