Feature-modules
[info] Objetivos do capítulo:
- demonstrar a modularização do software utilizando módulos
- criando componentes em módulos
- importando módulos
- descrevendo rotas em módulos
Todos os capítulos anteriores estão convergindo para um ponto: utilizar recursos do Angular para melhorar a arquitetura e estrutura do software. A figura a seguir dá uma ideia mais visual disso.
O funcionamento esperado é o seguinte:
- a interface gráfica é representada pelo arquivo
src/index.html
AppModule
, que é o root module, é apresentado na interface gráficaAppModule
importa os módulosAdminModule
ePublicoModule
.AdminModule
ePublicoModule
importam oSharedModule
e comunicam-se com a API HTTP REST.
Na prática, o AppModule
funciona como um concentrador dos demais módulos, já que ele tem poucas funcionalidades específicas, por isso os demais módulos, que fornecem funcionalidades, são chamados feature modules:
AdminModule
é responsável pelas funcionalidades administrativas (requerem acesso por login e senha);PublicoModule
contém funcionalidades que não requererem acesso por login e senha.SharedModule
contém funcionalidades que são utilizadas por outros módulos.
Na sequência o texto demonstra os elementos dessa arquitetura.
Estrutura do software
Apresentar uma figura ilustrando o diagrama de classes do software até aqui seria inútil por causa da quantidade de arquivos e artefatos do software até então. Entretanto, para ter uma ideia do tamanho e da complexidade do software como criado neste capítulo, as tabelas a seguir apresentam a relação de componentes em cada feature module e uma breve descrição de cada um deles.
Módulo Admin
Componente | Descrição |
---|---|
AdminComponent |
o shell component do módulo Admin |
CadastroDeDisciplinaComponent |
fornece um formulário para cadastro de uma disciplina |
CadastroDeTurmaComponent |
fornece um formulário para cadastro de uma turma |
DisciplinaComponent |
fornece a página inicial de uma disciplina, com dados para leitura ou consulta |
HomeComponent |
fornece a página inicial do módulo Admin |
ListaDeDisciplinasComponent |
fornece uma lista das disciplinas e dá acesso a outras funcionalidades: cadastrar, excluir e editar |
ListaDeTurmasComponent |
fornece uma lista das turmas e dá acesso a outras funcionalidades: cadastrar, excluir e editar |
PaginaNaoEncontradaComponent |
fornece uma página para ser apresentada quando uma URL não combinar com alguma das rotas do módulo Admin |
TurmaComponent |
fornece a página inicial de uma turma, com dados para leitura ou consulta |
AdminModule |
o módulo, em si |
AdminRoutingModule |
o módulo de rotas |
Disciplina |
o model de disciplina |
DisciplinasService |
o serviço com a lógica de negócio para o gerenciamento de disciplinas |
Turma |
o model de turma |
TurmasService |
o serviço com a lógica de negócio para o gerenciamento de turmas |
Módulo Público
Componente | Descrição |
---|---|
LoginComponent | fornece um formulário para autenticação e acesso ao sistema |
PublicoComponent | o shell component do módulo Publico |
PublicoModule | o módulo, em si |
PublicoRoutingModule | o módulo de rotas |
Módulo Shared
Component | Descrição |
---|---|
AuthService | o serviço com a lógica para autenticação do usuário |
SharedModule | o módulo, em si |
Estrutura de Navegação
Conforme o software aumenta na quantidade de componentes e funcionalidades torna-se interessante uma visão geral da navegação -- até mesmo porque estamos utilizando rotas. A figura a seguir ilustra a navegação entre as telas (ou páginas) do software.
Essa figura permite compreender que a primeira tela acessada é a tela de login. A partir dela o usuário tem acesso à tela home e às demais telas, como disciplinas e turmas. A partir dessas telas outras podem ser acessdas, como a página da disciplina ou a página da turma. As setas são bidirecionais para indicar os caminhos são de ida e volta.
Criando módulos
Para criar os elementos da arquitetura vamos utilizar o Angular CLI. Para começar, vamos criar os feature modules. Para fazer isso execute a linha de comando a seguir.
ng g m Admin --routing true
O comando cria o módulo Admin
, na pasta src/app/admin
, e os arquivos:
src/app/admin/admin.module.ts
: representa o módulo em sisrc/app/admin/admin-routing.module.ts
: representa o módulo de rotas
O capítulo anterior demonstrou como trabalhar com o recurso de rotas no Angular. Entretanto o capítulo utilizou o formato de descrever as rotas no próprio módulo AppModule
. Aqui neste capítulo, como há mais módulos, cada módulo possui, também, um módulo de rotas. Um módulo de rotas é um módulo que tem uma única responsabilidade: definir as rotas do módulo. Esse módulo será importado no feature module para que o Angular identifique corretamente as rotas e os seus componentes associados. O comando anterior especifica que deve ser criado um módulo de rotas por meio da opção --routing true
.
Criando componentes nos módulos
Para criar componentes em um módulo utilizamos o Angular CLI, usando a linha de comando a seguir:
ng g m Admin/Admin -m Admin --spec false
O comando cria o componente Admin
, na pasta src/app/admin/admin
e os arquivos do componente. Usar o Admin/Admin
é uma forma de indicar para o Angular CLI que o componente Admin
pertence ao módulo Admin
. A opção -m
é uma forma de garantir isso ainda mais. Além disso o comando também modifica o AdminModule
, inserindo o AdminComponent no array declarations
.
No padrão adotado neste livro todo feature module possui um componente de mesmo nome que é usado como shell component -- porque continuamos a usar o recurso de rotas.
Os demais componentes são criados de forma semelhante. Por exemplo, a criação do componente CadastroDeDisciplina:
ng g m Admin/CadastroDeDisciplina -m Admin --spec false
A linha de comando cria o componente CadastroDeDisciplina
na pasta src/app/admin/cadastro-de-disciplina
e modifica o AppModule
para incluir esse componente no array declarations
.
Definindo rotas
Com a utilização de feature modules o recurso de rotas ganha uma utilidade ainda mais marcante e evidente. O fundamento adotado pelo Angular é que cada feature module pode possuir seu módulo de rotas. Posteriormente, o root module poderá importar o feature module que desejar.
O módulo de rotas para o módulo Admin
, chamado AdminRouting
, contém um conteúdo semelhante ao seguinte (omitidas linhas com import
):
...
const routes: Routes = [
{
path: 'admin', component: AdminComponent, children: [
{path: 'disciplinas', component: ListaDeDisciplinasComponent},
{path: 'disciplinas/:id', component: DisciplinaComponent},
{path: 'disciplinas/:id/novo', component: CadastroDeDisciplinaComponent},
{path: 'disciplinas/:id/editar', component: CadastroDeDisciplinaComponent},
{path: 'cadastrar-turma', component: CadastroDeTurmaComponent},
{path: 'turmas', component: ListaDeTurmasComponent},
{path: 'turmas/:id', component: TurmaComponent},
{path: '', component: HomeComponent},
{path: '**', component: PaginaNaoEncontradaComponent}
]}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule {
}
Aqui o array routes
ganha uma característica diferenciada: o primeiro elemento, cujo atributo path
tem valor 'admin'
, está relacionado ao componente AdminComponent
e possui o atributo children
, que é um array de rotas. O recurso que permite definir esses tipos de rotas é chamado de rotas filhas. Assim, a rota 'admin'
possui várias rotas filhas. Além disso essa é a forma de indicar para o Angular que o AdminComponent
deve ser usado como shell component, ou seja, o Template dele contém o elemento router-outlet
.
Note também que há uma mudança na forma de indicar as rotas para os metadados do módulo: o atributo imports
contém uma chamada para RouterModule.forChild()
, que recebe como parâmetro o array routes
. Esse é uma diferença marcante para a forma de indicar as rotas para o root module, quando é utilizado o método RouterModule.forRoot()
.
Outro passo necessário é importar o módulo de rotas no feature module. A seguir, um trecho do AdminModule:
...
@NgModule({
imports: [
CommonModule,
FormsModule,
HttpClientModule,
NgbModule,
AdminRoutingModule,
SharedModule
],
...
})
export class AdminModule {
}
O penúltimo elemento do array imports
é o módulo de rotas (AdminRoutingModule
).
O módulo de rotas para o módulo Publico
, chamado PublicoRouting
, contém um conteúdo semelhante ao seguinte:
...
const routes: Routes = [
{
path: '', component: PublicoComponent, children: [
{path: '', component: LoginComponent}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PublicoRoutingModule {
}
O recurso de rotas filhas também é utilizado aqui. PublicoComponent
é o shell component. O fato interessante é que o atributo path
contém o mesmo valor ''
nas duas rotas. Como o Angular considera ''
a rota padrão, isso quer dizer que o PublicoModule
tem maior prioridade no momento em o Angular procurar encontrar uma rota com base na URL.
Incluindo as rotas dos feature modules no root module
O último passo desse processo é importar os feature modules no root module. A seguir, um trecho do AppModule
:
...
@NgModule({
imports: [
BrowserModule,
NgbModule.forRoot(),
FormsModule,
HttpClientModule,
AdminModule,
PublicoModule,
AppRoutingModule
],
...
})
export class AppModule {
}
Os módulos AdminModule
, PublicoModule
e AppRoutingModule
são importados por meio do atributo imports
dos metadados da classe AppModule
. Como os dois primeiros importam seus respectivos módulos de rotas, o root module também tem conhecimento delas e, assim, consegue aplicar o mesmo processo anterior de procura de rotas quando o browser fornece uma URL.
Um aspecto importante é que a importação segue uma ordem. Veja que, primeiro, é importado o módulo AdminModule
, depois o PublicoModule
e, por fim, o AppRoutingModule
. Como você viu no capítulo anterior o processo de procura de rotas percorre uma lista de rotas do início até encontrar uma rota correspondente. Nesse caso a ordem das importações dos módulos quer dizer para o Angular considerar, primeiro, as rotas do AdminModule
. Isso é muito importante porque as rotas do PublicoModule
, por serem a rota padrão, devem estar no final do processo de procura de rotas.
Shared modules
O Angular utiliza o conceito de shared modules para indicar módulos que fornecem recursos que são usados por outros. Conforme a arquitetura do software até o momento o serviço AuthService
implementa a lógica de negócio de autenticação. Para ele poder ser usado nos módulos AdminModule
e PublicoModule
esse elemento do software deve estar em um módulo compartilhado. Se ele estivesse, por exemplo, no módulo PublicoModule
e o AdminModule
o importasse isso geraria problemas de ordem ou ciclos de importação.
Assim, a solução é isolar o AuthService em outro módulo, no caso o SharedModule. Na prática, um shared module é também um feature module, mas, provavelmente, ele apenas fornecerá recursos para serem utilizados em outros feature modules.
Posteriormente, cada feature module importa o shared module quando precisar utilizar seus recursos.
Fluxo de trabalho
O fluxo de trabalho do capítulo anterior acaba de ser atualizado -- e também aumentou em complexidade. Da mesma forma como antes, seguir os passos do processo também ajuda a compreendê-lo:
- criar feature module: essa agora é a primeira coisa a fazer;
- criar componente: crie os componentes do feature module. Continue conduzido esse processo de forma iterativa, não se preocupe em construir o componente por inteiro, deixe-o como criado pelo Angular CLI;
- definir rotas: depois, defina as rotas no módulo de routas;
- implementar a lógica de negócio em um serviço: utilize serviços para implementar a lógica de negócio; e
- implementar lógica do componente e usar o serviço: volte a trabalhar com cada compomente.