Injeção de dependência em Angular: um guia abrangente

Injeção de dependência em Angular: um guia abrangente

Aprenda como dominar a injeção de dependência em Angular com nosso tutorial fácil de seguir. Descubra os benefícios desta técnica poderosa e leve o desenvolvimento de seu aplicativo para o próximo nível.

Imagem em destaque

Angular é uma estrutura de código aberto para a construção de aplicativos web modernos. Um dos princípios-chave do Angular é a injeção de dependência. É um padrão de design que permite a criação de aplicações eficientes e escaláveis.

Neste guia abrangente, exploraremos o que é injeção de dependência no fluxo de trabalho de uma empresa de desenvolvimento Angular, os benefícios, a implementação e as melhores práticas. Quer você seja novo no trabalho em uma empresa de desenvolvimento Angular ou seja um desenvolvedor experiente, este guia o ajudará a dominar a arte de criar aplicativos eficientes e escalonáveis ​​usando injeção de dependência Angular.

Vamos mergulhar mais fundo no mundo da injeção de dependência!

Visão geral da injeção de dependência angular

O que é injeção de dependência?

A injeção de dependência (DI) é um padrão de design. A injeção de dependência angular é um mecanismo onde são criadas injeções de dependência de componentes ou serviços. Em seguida, ele é injetado nele em tempo de execução, em vez de ser criado dentro do serviço ou componente. Isso permite a criação de um código modular e fracamente acoplado. Isso significa que os componentes e serviços só precisam se preocupar com quais são as dependências, em vez de criá-las.

Existem três tipos de métodos de injeção de dependência em Angular que podem ser utilizados para fornecer dependências a componentes e serviços. Eles são os seguintes.

Tipo de injeção Descrição
Injeção de Construtor Ele fornece dependências usando a função construtora de classe.
Injeção de Setter Um método setter é usado para injetar a dependência pelo injetor.
Injeção de interface A dependência fornece um método injetor que injeta dependência em qualquer cliente passado para ela. O cliente também precisa ter um método setter pronto para aceitar a injeção de dependência Angular.

O uso dos métodos acima varia de acordo com o cenário e também depende dos requisitos do aplicativo. Vejamos os benefícios da injeção de dependência no Angular.

Os benefícios do uso de injeção de dependência em Angular

A injeção de dependência oferece muitos benefícios críticos e importantes para aplicativos Angular. Vejamos alguns deles.

  1. O componente e a classe de serviço são mais modulares.
  2. Configuração complexa e maquete são desnecessárias; podemos testar isoladamente, pois é muito mais fácil.
  3. Devido à modularidade do código, ele pode ser reutilizado com muita facilidade.
  4. Codebase agora é mais fácil de gerenciar.

Compreendendo a Inversão de Controle (IoC) e seu papel no DI

Na Inversão de Controle (IoC), o componente ou serviço não é responsável por gerenciar suas dependências. Portanto, deve ser injetado em um recipiente ou estrutura separada. É um princípio de design intimamente ligado à injeção de dependência. A responsabilidade deveria ser invertida. Isso significa que deve ser injetado em um contêiner ou estrutura separada.

Um exemplo de IoC e DI em Angular pode ser visto no código a seguir.

import { Component } from '@angular/core';
import { ProductService } from './product.service';

@Component({
  selector: 'app-product-list',
  templateUrl: './product-list.component.html',
})
export class ProductListComponent {
  products: any ;

  constructor(private productService: ProductService) {}

  ngOnInit  {
    this.products = this.productService.getProducts ;
  }
}

Neste exemplo, o ProductListComponent depende da classe de exportação ProductServiceProductService. Em vez de criar uma instância do ProductService dentro do ProductListComponent. A função construtora é usada para injetar ProductService no componente por meio do construtor.

O próprio ProductService pode ter dependências de outros serviços, mas não precisa se preocupar em criar essas dependências porque a estrutura DI é responsável por criar as instâncias desses serviços e injetá-las no ProductService.

Conceitos básicos na injeção de dependência do Angular

A injeção de dependência é facilitada por vários conceitos básicos. É importante compreender esses conceitos para utilizar efetivamente a injeção de dependência Angular.

Provedores

Provedores são objetos responsáveis ​​por criar e gerenciar instâncias de dependências que podem ser injetadas em componentes e serviços. Ele pode ser definido em nível de componente, módulo ou aplicativo. Você precisa usar a propriedade Provider para implementá-la em Angular. Vejamos um exemplo para ver como ele é implementado.

import { Component, Injectable } from '@angular/core';

@Injectable 
export class MyService {
  getData  {
    return "Data from MyService";
  }
}

@Component({
  selector: 'my-component',
  providers: (MyService),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(private myService: MyService) {}

  data = this.myService.getData ;
}

O exemplo acima define uma classe MyService com um decorador @Injectable. A classe MyService deve ser injetada como uma dependência pelo Angular.

Propriedade useClass

Também é importante especificar quais classes serão usadas como dependência. Vejamos a propriedade useClass.

import { Component, Injectable } from '@angular/core';

@Injectable 
export class MyService {
  getData  {
    return "Data from MyService";
  }
}

@Injectable 
export class MyOtherService {
  getData  {
    return "Data from MyOtherService";
  }
}

@Component({
  selector: 'my-component',
  providers: ({ provide: MyService, useClass: MyOtherService }),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(private myService: MyService) {}

  data = this.myService.getData ;
}

Propriedade useValue

Esta propriedade é usada para especificar um valor que deve ser usado como dependência. Um exemplo de implementação é o seguinte:

import { Component } from '@angular/core';

const myValue = "Data from useValue";

@Component({
  selector: 'my-component',
  providers: ({ provide: 'MyValue', useValue: myValue }),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(@Inject('MyValue') private data: string) {}
}

No exemplo acima, a propriedade useValue Providers especifica e fornece um valor de string. O valor é injetado na propriedade data usando o decorador @Inject.

Propriedade useFactory

Esta propriedade é usada para especificar uma função de fábrica que deve ser usada para criar uma dependência. Vejamos um exemplo.

import { Component } from '@angular/core';

export function myFactory  {
  return "Data from useFactory";
}

@Component({
  selector: 'my-component',
  providers: ({ provide: 'MyValue', useFactory: myFactory }),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(@Inject('MyValue') private data: string) {}
}

O atributo useFactory é usado para definir e fornecer uma função de fábrica no exemplo anterior. O decorador @Inject instrui o aplicativo a definir a propriedade data com o valor retornado pela função de fábrica.

propriedade useExistente

Esta propriedade é usada para especificar uma dependência existente que deve ser usada como valor da nova dependência. Vejamos um exemplo.

import { Component, Injectable } from '@angular/core';

@Injectable 
export class MyService {
  getData  {
    return "Data from MyService";
  }
}

@Injectable 
export class MyOtherService {
  getData  {
    return "Data from MyOtherService";
  }
}

@Component({
  selector: 'my-component',
  providers: (MyService, { provide: MyOtherService, useExisting: MyService }),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(private myOtherService: MyOtherService) {}

  data = this.myOtherService.getData ;
}

O decorador @Injectable é usado na definição de MyService e MyOtherService no exemplo anterior. Usando a propriedade useExisting, a classe MyComponent declara que MyService deve ser usado como o valor de MyOtherService.

Injetores

O injetor é responsável por criar e gerenciar dependências. Um aplicativo inteiro possui um injetor raiz que o Angular cria automaticamente. Todos os outros injetores criados posteriormente são filhos do injetor raiz. Vejamos um exemplo.

import { Component, Injectable, Injector } from '@angular/core';

@Injectable 
export class MyService {
  getData  {
    return "Data from MyService";
  }
}

@Component({
  selector: 'my-component',
  template: '{{ data }}'
})
export class MyComponent {
  constructor(private injector: Injector) {}

  data = this.injector.get(MyService).getData ;
}

No código acima, a classe MyService é definida com um decorador @Injectable. Isso significa que a classe pode ser injetada como uma dependência. A classe MyComponent define uma dependência do Injector usando o construtor para injetar uma instância do Injector. A propriedade data é então definida como o resultado da chamada do método getData em uma instância de MyService que é recuperada usando o método get na instância do injetor.

Fichas

O token de injeção de dependência em Angular é usado para identificar uma dependência representada por uma string ou classe. É importante observar que o token de injeção é usado com a propriedade fornecida para especificar qual dependência deve ser usada para o token fornecido. Vejamos o exemplo de implementação da seguinte maneira:

import { Component, Injectable, Inject } from '@angular/core';

export const MY_TOKEN = 'myToken';

@Injectable 
export class MyService {
  getData  {
    return "Data from MyService";
  }
}

@Component({
  selector: 'my-component',
  providers: ({ provide: MY_TOKEN, useClass: MyService }),
  template: '{{ data }}'
})
export class MyComponent {
  constructor(@Inject(MY_TOKEN) private myService: MyService) {}

  data = this.myService.getData ;
}

No exemplo de código acima, a classe MyService informa ao Angular que esta classe pode ser injetada como uma dependência. A classe MyComponent define uma dependência de MyService especificando-o como o valor da propriedade fornecida usando o token MY_TOKEN. O decorador @Inject é usado para injetar uma instância de MyService na propriedade myService da classe MyComponent. A propriedade data é então definida como o resultado da chamada do método getData em myService.

Implementando injeção de dependência em Angular

Esta seção do artigo se concentrará na implementação da injeção de dependência Angular.

Pré-requisitos

Você deve ter o seguinte:

  • Node.js: A última versão do Node.js em sua máquina.
  • Um editor de código: Qualquer IDE que suporte Angular.
  • npm: Node Package Manager para instalar as dependências necessárias.
  • CLI angular: A versão mais recente que fornece núcleo Angular.

Instale as dependências necessárias

Se você não tiver o Angular pré-instalado em sua máquina. Use o seguinte comando no terminal para instalar o Angular CLI:

npm install -g @angular/cli

Crie um novo projeto

Para criar um novo projeto angular e um aplicativo inicial, execute o comando CLI ng new e forneça o nome my-app.

ng new my-app

Seu arquivo package.json deve ser parecido com isto:

Observe que as versões das dependências são especificadas no arquivo acima. Estas eram as versões no momento da criação deste guia.

Implementação

Em seguida, um serviço precisa ser criado para que a injeção possa ser configurada para os componentes. Serviços são classes que fornecem funcionalidade para todo o aplicativo. Você pode criar um serviço usando o Angular CLI executando o seguinte comando.

ng generate service my-service

Depois que o serviço for criado, você precisará registrá-lo no sistema de injeção de dependência do Angular. Você pode fazer isso adicionando-o à matriz de provedores no módulo app. O componente raiz é adicionado aqui por padrão. Abra o arquivo app.module.ts e adicione a seguinte linha.

import { MyServiceService } from './my-service.service';

@NgModule({
  declarations: (
    AppComponent
  ),
  imports: (
    BrowserModule,
    AppRoutingModule
  ),
  providers: (MyServiceService), // add the service here
  bootstrap: (AppComponent)
})
export class AppModule { }

Injetando Dependências em Componentes e Serviços

Agora que o serviço está configurado e registrado no sistema de injeção de dependência. Ele pode ser injetado no componente pai ou nos componentes filhos. Para fazer isso, você deve adicionar um construtor ao componente e especificar o serviço como parâmetro. Por exemplo, pense em um componente MyComponent que precisa usar o serviço MyService. Você pode injetar o serviço no componente assim. Você deve adicionar isso no arquivo ts do meu componente.

import { Component } from '@angular/core';
import { MyServiceService } from '../my-service.service';

@Component({
  selector: 'my-component',
  template: '<p>{{ getMessage  }}</p>'
})
export class MyComponent {

  constructor(private myService: MyServiceService) {}

  getMessage : string {
    return this.myService.getMessage ;
  }
}

No construtor do componente, MyServiceService é especificado como parâmetro. Também é definido um método getMessage que busca os dados do serviço.

A seguir, em vez de registrar um serviço com um módulo, podemos usar provideIn para registrar o serviço com o injetor root. Podemos fazer isso especificando provideIn: 'root' no decorador @Injectable do nosso serviço.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyServiceService {
  getMessage : string {
    return 'Hello from MyService!';
  }
}

Isso registrará o serviço com o injetor root. Isso o torna disponível para qualquer componente em nosso aplicativo. Você também pode buscar dados nos serviços de fontes externas. É assim que você registra serviços no Angular.

Injeção de Dependência em Diretivas e Pipes

Para injetar serviços e outras dependências nas diretivas e canais, você precisa adicionar um construtor e especificar as dependências como parâmetros.

Por exemplo, digamos que você tenha uma diretiva personalizada que precise usar o serviço MyService. Você pode injetar o serviço na diretiva assim.

Primeiro, crie um novo arquivo de diretiva custom.directive.ts na pasta do aplicativo. Adicione o seguinte código.

import { Directive, Input, ElementRef } from '@angular/core';

@Directive({
  selector: '(customDirective)'
})
export class CustomDirective {
  @Input  customDirective: string;

  constructor(private el: ElementRef) {
    this.customDirective="";
  }

  ngOnInit  {
    this.el.nativeElement.style.color = this.customDirective;
  }
}

Esta diretiva recebe uma entrada do tipo string e a utiliza para alterar a cor do elemento ao qual é aplicada.

A seguir, no arquivo app.module.ts, importe o CustomDirective e adicione-o às declarações e matrizes de exportação. Este arquivo é o módulo raiz do aplicativo. Nas declarações você pode ver todos os componentes e a diretiva.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { MyServiceService } from './my-service.service';
import { MyComponent } from './my-component/my-component.component';
import { CustomDirective } from './custom.directive';

@NgModule({
  declarations: (
    AppComponent,
    MyComponent,
    CustomDirective // add here
  ),
  imports: (
    BrowserModule,
  ),
  providers: (MyServiceService),
  bootstrap: (AppComponent)
})
export class AppModule { }

Por fim, no arquivo app.component.html, aplique a diretiva a um elemento e passe a cor desejada. Adicione my-component também.

<h1 customDirective="red">Hello World!</h1>
<my-component></my-component>

Execute o aplicativo

Abra uma janela de terminal e navegue até o diretório raiz do seu projeto. Execute o comando ng serve para iniciar o servidor de desenvolvimento.

Abra um navegador da web e navegue até para visualizar o aplicativo.

É assim que a página da web deve ser.

Injeção de Dependência Hierárquica

A injeção de dependência cria uma estrutura de injetores semelhante a uma árvore de componentes. Cada injetor de componente pode acessar o serviço fornecido pelos injetores pai e raiz. Sempre que uma classe de componente solicita um serviço, o Angular o procura no injetor atual. Se não for encontrado, ele pesquisará na árvore do injetor até que um provedor seja encontrado.

Teste com injeção de dependência

TestBed é um utilitário fornecido pela Angular para testes. Ele permite configurar e criar um módulo de teste. Você pode usar o método TestBed.configureTestingModule para configurar o módulo de teste com as dependências e provedores necessários.

Vamos ver como os testes são feitos.

Testando um componente com serviço injetado.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponent } from './my.component';
import { MyService } from './my.service';

describe('MyComponent',   => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;

  beforeEach(async   => {
    await TestBed.configureTestingModule({
      declarations: ( MyComponent ),
      providers: ( MyService )
    })
    .compileComponents ;
  });

  beforeEach(  => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges ;
  });

  it('should create',   => {
    expect(component).toBeTruthy ;
  });

  it('should use injected service',   => {
    const service = TestBed.inject(MyService);
    spyOn(service, 'getData').and.returnValue('test data');
    expect(component.getDataFromService ).toEqual('test data');
  });
});

Testando um serviço com dependências injetadas.

import { TestBed } from '@angular/core/testing';
import { MyService } from './my.service';
import { HttpClient } from '@angular/common/http';
import { of } from 'rxjs';

describe('MyService',   => {
  let service: MyService;
  let httpSpy: jasmine.SpyObj<HttpClient>;

  beforeEach(  => {
    httpSpy = jasmine.createSpyObj('HttpClient', ('get'));
    TestBed.configureTestingModule({
      providers: (
        MyService,
        { provide: HttpClient, useValue: httpSpy }
      )
    });
    service = TestBed.inject(MyService);
  });

  it('should be created',   => {
    expect(service).toBeTruthy ;
  });

  it('should return expected data',   => {
    const expectedData = { message: 'test message' };
    httpSpy.get.and.returnValue(of(expectedData));
    service.getData .subscribe((data) => {
      expect(data).toEqual(expectedData);
    });
  });
});

Em alguns casos, você pode querer testar um componente ou serviço isoladamente de suas dependências. Para fazer isso, você pode usar espiões ou serviços simulados. Uma função espiã registra todas as chamadas feitas a ela e depois é usada para afirmá-las em testes. Um serviço simulado é uma implementação de serviço falsa que permite controlar seu comportamento e retornar valores.

Melhores práticas e armadilhas comuns

É importante abordar algumas práticas recomendadas e armadilhas comuns ao trabalhar em Angular.

  • Distinguir entre serviços e seus prestadores é essencial. Isso tem o potencial de simplificar o teste e a manutenção do código.
  • Cada serviço deve ser criado apenas uma vez e compartilhado no aplicativo, o que pode diminuir significativamente a sobrecarga e aumentar a velocidade.
  • Dependências circulares são uma armadilha típica que deve ser evitada ao usar injeção de dependência. Isso pode acontecer se dois ou mais serviços dependerem um do outro de alguma forma. Como resultado, cria-se um círculo vicioso de dependência, que pode ser difícil de controlar e levar a erros. Para evitar isso, é crucial examinar as interdependências entre os serviços e organizá-los de forma que não haja loops.

Se você gostou deste artigo, confira:

  • Dominando o roteamento angular: um guia abrangente
  • Estrutura angular do projeto: práticas recomendadas para arquivos e pastas
  • Dominando a vinculação angular de dados: um guia abrangente para especialistas
  • Principais bibliotecas e estruturas de componentes de UI Angular
  • O que é Angular e por que sua empresa deve considerá-lo para desenvolvimento?
  • Os melhores frameworks Javascript da atualidade
  • Angular para negócios
  • Qual é a melhor estrutura para desenvolvimento web?

Conclusão

Compreender e implementar a injeção de dependência em Angular é fundamental para construir aplicativos escalonáveis ​​e de fácil manutenção, esteja você diretamente envolvido ou terceirizando o desenvolvimento de software Angular. Por meio deste guia, cobrimos os conceitos básicos da injeção de dependência do Angular e um exemplo de implementação para esclarecer o entendimento.

Seguindo o guia passo a passo e o exemplo, você deve entender como usar a injeção de dependência em seus próprios projetos Angular ou ao terceirizar o desenvolvimento de software Angular. Com esse conhecimento, os desenvolvedores podem criar aplicativos mais eficientes, flexíveis e mais fáceis de manter.

Fonte: BairesDev

Conteúdo Relacionado

O Rails 8 sempre foi um divisor de águas...
A GenAI está transformando a força de trabalho com...
Entenda o papel fundamental dos testes unitários na validação...
Aprenda como os testes de carga garantem que seu...
Aprofunde-se nas funções complementares dos testes positivos e negativos...
Vídeos deep fake ao vivo cada vez mais sofisticados...
Entenda a metodologia por trás dos testes de estresse...
Descubra a imprevisibilidade dos testes ad hoc e seu...
A nomeação de Nacho De Marco para o Fast...
Aprenda como os processos baseados em IA aprimoram o...
A web está em constante evolução, e com ela,...
A Inteligência Artificial (IA) tem sido um tema cada...
Você já se sentiu frustrado com a complexidade de...
O OpenStack é uma plataforma de computação em nuvem...
Você já se sentiu frustrado com a criação de...
A era digital trouxe uma transformação profunda na forma...
Nos dias atuais, a presença digital é fundamental para...
Introdução Quando se trata de desenvolvimento de software, a...
Como desenvolvedor Dart, você provavelmente já se deparou com...
ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。