
O Angular 6 introduziu uma nova sintaxe aprimorada para incorporar dependências de serviço em um aplicativo (
allowIn ). Apesar do fato de o Angular 7 já ter sido lançado, esse tópico ainda é relevante.
Há muita confusão nos comentários do GitHub, Slack e Stack Overflow, então vamos dar uma olhada mais de perto neste tópico.
Neste artigo, consideraremos:
- Injeção de dependência
- Maneira antiga de injetar dependências no Angular ( provedores: [] );
- Uma nova maneira de injetar dependências no Angular ( fornecido em: 'root' | SomeModule );
- Os cenários UseIn fornecem ;
- Recomendações para usar a nova sintaxe em aplicativos;
- Para resumir.
Injeção de Dependência
Você pode pular esta seção se já tiver uma idéia sobre o
DI .
A injeção de dependência ( DI ) é uma maneira de criar objetos que dependem de outros objetos. O sistema de injeção de dependência fornece objetos dependentes quando instancia uma classe.
- Documentação angular
Explicações formais são boas, mas vamos dar uma olhada no que é injeção de dependência.
Todos os componentes e serviços são classes. Cada classe possui um método
construtor especial, que, quando chamado, cria um objeto de instância dessa classe, que é usado no aplicativo.
Suponha que em um de nossos serviços haja o seguinte código:
constructor(private http: HttpClient)
Se você o criar sem usar o mecanismo de injeção de dependência, deverá adicionar o
HttpClient manualmente. Em seguida, o código ficará assim:
const myService = new MyService(httpClient)
Mas onde, neste caso, para obter
httpClient ? Também precisa ser criado:
const httpClient = new HttpClient(httpHandler)
Mas onde obter o
httpHandler agora? E assim por diante, até que todas as classes necessárias sejam instanciadas. Como podemos ver, a criação manual pode ser complicada e erros podem ocorrer no processo.
O mecanismo de injeção de dependência angular faz tudo isso automaticamente. Tudo o que precisamos fazer é especificar as dependências no construtor do componente e elas serão adicionadas sem nenhum esforço de nossa parte.Maneira antiga de injetar dependências no Angular (provedores: [])
Para executar o aplicativo, o Angular precisa conhecer cada objeto individual que queremos implementar em componentes e serviços. Antes do lançamento do Angular 6, a única maneira de fazer isso era especificar os serviços na propriedade de
provedores: [] decoradores
@NgModule ,
@ omponent e
@Directive .

Aqui estão três principais usos dos
provedores: [] :
- No decorador @NgModule do módulo imediatamente carregado ( ansioso );
- No decorador @NgModule do módulo de carregamento atrasado ( lento );
- Nos decoradores @Component e @Directive .
Módulos baixados com o aplicativo (Eager)
Nesse caso, o serviço é registrado no escopo global como singleton. Será um singleton, mesmo se incluído nos
provedores [] de vários módulos. É criada uma única instância da classe de serviço que será registrada no nível raiz do aplicativo.
Módulos de Carga Diferida (Preguiçoso)
Uma instância do serviço conectado ao módulo
lento será criada durante sua inicialização. Adicionar um serviço desse tipo ao componente
ansioso do módulo resultará em um erro:
Nenhum provedor para MyService! erro .
Implementação em @ Component e @ Directiva
Quando implementada em um componente ou diretiva, é criada uma instância separada do serviço, que estará disponível neste componente e em todos os filhos.
Nessa situação, o serviço não será um singleton, sua instância será criada sempre que o componente for usado e excluído, juntamente com a remoção do componente do DOM.
Nesse caso, o
RandomService não
é implementado no nível do módulo e não é um singleton,
mas registrado com
provedores: [] do componente
RandomComponent . Como resultado, obteremos um novo número aleatório sempre que usarmos
<rand®m> </ rand®m> .
Nova maneira de injetar dependências no Angular (fornecido em: 'root' | SomeModule)
No Angular 6, obtivemos uma nova ferramenta
"Provedores que podem ser
sacudidos em árvore" para
injetar dependências em um aplicativo, que pode ser usado usando a propriedade
fornecidaIn do decorador
@Injectable .
Você pode imaginar fornecido como a implementação de dependências na direção oposta: antes que o módulo descrevesse os serviços nos quais será conectado, agora o serviço define o módulo ao qual está conectado.O serviço pode ser incorporado na raiz do aplicativo (
fornecidoIn: 'root' ) ou em qualquer módulo (
fornecidoIn: SomeModule ).
fornecidoIn: 'root' é uma abreviação para implementação no
AppModule .

Vamos analisar os principais cenários para usar a nova sintaxe:
- Implementação no módulo raiz do aplicativo ( fornecido em: 'raiz' );
- Implementação no módulo imediatamente carregado ( ansioso );
- Implementação no módulo com carregamento atrasado ( lento ).
Implementação no módulo raiz do aplicativo (fornecido em: 'root')
Esta é a opção de injeção de dependência mais comum. Nesse caso, o serviço será adicionado ao aplicativo de pacote configurável apenas se for realmente usado, ou seja, incorporado em um componente ou outro serviço.
Ao usar a nova abordagem, não haverá muita diferença em um aplicativo SPA monolítico, onde todos os serviços escritos são usados, no entanto,
desde que In
: 'root' seja útil ao escrever bibliotecas.
Anteriormente, todos os serviços de biblioteca precisavam ser adicionados aos
provedores: [] de seu módulo. Após importar a biblioteca para o aplicativo, todos os serviços foram adicionados ao pacote, mesmo que apenas um fosse usado. No caso de
allowedIn: 'root', não há necessidade de conectar o módulo da biblioteca. Basta incorporar o serviço no componente desejado.
Módulo de carregamento atrasado (lento) e fornecidoIn: 'root'
O que acontece se você implementar o serviço com o item
fornecido: raiz no módulo
lento ?
Tecnicamente,
'root' significa
AppModule , mas Angular é inteligente o suficiente para adicionar um serviço ao pacote
lento de um módulo se ele for implementado apenas em seus componentes e serviços. Mas há um problema (embora algumas pessoas afirmem que esse é um recurso). Se posteriormente você introduzir o serviço usado apenas no módulo
lento no módulo principal, o serviço será transferido para o pacote principal. Em aplicativos grandes com muitos módulos e serviços, isso pode levar a problemas de rastreamento de dependência e comportamento imprevisível.
Cuidado! A implementação de um serviço em vários módulos pode levar a dependências ocultas difíceis de entender e impossíveis de desvendar.Felizmente, existem maneiras de evitar isso, e as consideraremos abaixo.
Injeção de dependência no módulo imediatamente carregado (ansioso)
Como regra geral, este caso não faz sentido e, em vez disso, podemos usar a opção
fornecido em: 'root' . A conexão de um serviço no
EagerModule pode ser usada para encapsulamento e impedirá a implementação sem a conexão de um módulo, mas na maioria dos casos isso não é necessário.
Se você realmente precisar limitar o escopo do serviço, é mais fácil usar o antigo
provedor: [] , pois certamente não levará a dependências cíclicas.
Se possível, tente usar o método fornecido em: 'root' em todos os módulos ansiosos.Nota A vantagem dos módulos de carregamento atrasado (lento)
Uma das principais características do Angular é a capacidade de dividir facilmente o aplicativo em fragmentos, o que oferece as seguintes vantagens:
- O tamanho pequeno do pacote principal do aplicativo, devido ao qual o aplicativo é carregado e iniciado mais rapidamente;
- O módulo de carregamento atrasado está bem isolado e é conectado ao aplicativo uma vez na propriedade loadChildren da rota correspondente.
Graças ao atraso no carregamento, um módulo inteiro com centenas de serviços e componentes pode ser removido ou movido para um aplicativo ou biblioteca separado, com pouco ou nenhum esforço.Outra vantagem do isolamento do módulo
lento é que um erro cometido nele não afetará o restante do aplicativo. Agora você pode dormir em paz, mesmo no dia do lançamento.
Implementação em um módulo com carregamento atrasado (fornecido em: LazyModule)

A injeção de dependência em um módulo específico impede o uso do serviço em outras partes do aplicativo. Isso preserva a estrutura de dependência, que é especialmente útil para aplicativos grandes em que a injeção de dependência desarrumada pode causar confusão.
Fato interessante: Se você implementar o serviço lento na parte principal do aplicativo, o assembly (mesmo AOT) falhará sem erros, mas o aplicativo falhará com o erro "Nenhum provedor para LazyService".O problema com dependência cíclica

Você pode reproduzir o erro da seguinte maneira:
- Crie o módulo LazyModule ;
- Criamos o serviço LazyService e nos conectamos usando fornecidoIn: LazyModule ;
- Criamos o componente LazyComponent e o conectamos ao LazyModule ;
- Adicione LazyService ao construtor do componente LazyComponent ;
- Temos um erro com uma dependência cíclica.
Esquematicamente, fica assim:
service -> module -> component -> service .
Você pode resolver esse problema criando um submódulo
LazyServiceModule , que será conectado ao
LazyModule . Conecte serviços ao submódulo.

Nesse caso, você precisará criar um módulo adicional, mas ele não exigirá muito esforço e dará as seguintes vantagens:
- Isso impedirá a introdução do serviço em outros módulos de aplicativos;
- Um serviço será incluído no pacote configurável apenas se estiver incorporado em um componente ou outro serviço usado no módulo.
Incorporando um serviço em um componente (fornecido em: SomeComponent)
É possível incorporar um serviço no
@Component ou no
@Directive usando a nova sintaxe?
Não no momento!Para criar uma instância do serviço para cada componente, você ainda precisa usar
provedores: [] nos
decoradores @ omponent ou
@Directive .

Práticas recomendadas para o uso de nova sintaxe em aplicativos
Bibliotecas
allowedIn: 'root' é bom para criar bibliotecas. Essa é uma maneira realmente conveniente de conectar apenas a parte da funcionalidade usada diretamente ao aplicativo principal e reduzir o tamanho da montagem final.
Um exemplo prático é a biblioteca do modelo ngx , que foi reescrita usando a nova sintaxe e agora é chamada @ angular-extensions / model . Na nova implementação, não há necessidade de conectar o NgxModelModule ao aplicativo, basta incorporar o ModelFactory no componente necessário. Detalhes da implementação podem ser encontrados aqui .Módulos de Download Adiado (lento)
Use o módulo separado
fornecido em: LazyServicesModule para serviços e conecte-o ao
LazyModule . Essa abordagem encapsula serviços e evita que eles sejam conectados a outros módulos. Isso definirá limites e ajudará a criar uma arquitetura escalável.
De acordo com minha experiência, a introdução acidental no módulo principal ou adicional (usando o método fornecidoIn: 'root') pode causar confusão e não é a melhor solução!fornecido no: 'root' também funcionará corretamente, mas ao usar o
fornecido no LazyServideModule , obtemos um erro de
"provedor ausente" quando implementado em outros módulos e podemos corrigir a arquitetura.
Mova o serviço para um local mais apropriado na parte principal do aplicativo.Quando os provedores devem ser usados: []?
Nos casos em que é necessário configurar o módulo. Por exemplo, conecte o serviço apenas a
SomeModule.forRoot (someConfig) .
Por outro lado, nessa situação, você pode usar a opção fornecido em: 'root'. Isso garantirá que o serviço seja adicionado ao aplicativo apenas uma vez.Conclusões
- Use a opção allowedIn: 'root' para registrar o serviço como um singleton, disponível em todo o aplicativo.
- Para o módulo incluído no pacote principal, use fornecidoIn: 'root' , não fornecidoIn: EagerlyImportedModule . Em casos excepcionais, use provedores: [] para encapsulamento.
- Crie um submódulo com serviços para limitar seu escopo fornecido em: LazyServiceModule ao usar carregamento lento .
- Conecte o módulo LazyServiceModule ao LazyModule para evitar dependência circular.
- Use provedores: [] nos decoradores @ omponent e @Directive para criar uma nova instância de serviço para cada nova instância de componente. Uma instância de serviço também estará disponível em todos os componentes filhos.
- Sempre limite o escopo das dependências para melhorar a arquitetura e evitar dependências confusas.
Referências
Artigo originalAngular é uma comunidade de língua russa.Meetups de Angular em Rússia