O time do Angular já tinha em seu Roadmap a ideia de tornar os módulos opcionais e recentemente criaram uma discussão no GitHub para apresentar uma proposta de uso e coletar o feedback da comunidade.
Além de Componentes, a proposta também estende-se para Diretivas e Pipes. Cada um desses elementos poderia ser um módulo em si.
Quando comecei a usar Angular não sabia bem como organizar os elementos entre os módulos. Acabava criando mega-módulos com muitas declarações e imports. Assim era difícil saber quais imports eram usados por quais componentes ou se algum import estava lá de forma desnecessária.
Após essa experiência comecei a criar módulos com apenas 1 componente, dessa forma consigo saber exatamente o que um determinado componente precisa. Eu costumo declarar tudo em um mesmo arquivo *.module.ts
, separando o template em um .html
nos casos onde ele fica grande ou com muita identação. Ex:
app-text-field.module.ts
@Component({
selector: 'app-text-field',
template: `
<mat-form-field>
<mat-label></mat-label>
<input matInput [formControl]="control" [placeholder]="placeholder ?? label">
</mat-form-field>
`
})
export class AppTextFieldComponent{
@Input() placeholder?: string;
@Input() label!: string;
@Input() control!: AbstractControl;
}
@NgModule({
declarations: [AppTextFieldComponent],
imports: [MatFormFieldModule,MatInputModule,ReactiveFormsModule],
exports: [AppTextFieldComponent]
})
export class AppTextFieldModule { }
Lendo a discussão acabei descobrindo que este padrão de 1 módulo com 1 componente já tem um nome: SCAM (single-component Angular module).
A proposta de módulos opcionais apresenta uma nova flag standalone
no decorador @Component
(e também nos decoradores @Directive
e @Pipe
). Ela indica que o componente já é auto-contido, ou seja, ele mesmo é um módulo que pode ser importado por outros módulos ou outros elementos standalone.
Como este tipo de elemento não tem mais um módulo onde é declarado juntamente com suas dependências, esses decoradores também terão o campo imports
(na discussão é solicitado opinião da comunidade sobre mudar este nome para deps
ou uses
).
Nesse caso, o exemplo acima não precisaria mais de um módulo. Eu poderia trocar o nome do arquivo para o padrão *.component.ts
e reescrevê-lo assim:
app-text-field.component.ts
@Component({
selector: 'app-text-field',
standalone: true,
imports: [MatFormFieldModule,MatInputModule,ReactiveFormsModule],
template: `
<mat-form-field>
<mat-label></mat-label>
<input matInput [formControl]="control" [placeholder]="placeholder ?? label">
</mat-form-field>
`
})
export class AppTextFieldComponent{
@Input() placeholder?: string;
@Input() label!: string;
@Input() control!: AbstractControl;
}
Outra regra que costumo seguir nos meus desenvolvimentos é que toda página deve ser carregada utilizando lazy loading. Hoje na prática isso significa usar a opção loadChildren
em vez da opção component
.
A proposta apresenta uma nova opção loadComponent
onde é possível apontar um standalone component.
Ou seja, se hoje eu faço uma página desta forma:
pagina1.module.ts
@Component({
template: 'Olá!'
})
export class Pagina1Component{}
const routes: Routes = [{
path: '',
component: Pagina1Component
}]
@NgModule({
declarations: [Pagina1Component],
imports: [RouterModule.forChild(routes)],
})
export class Pagina1Module { }
app-routing.module.ts
const routes: Routes = [
...,
{
path: 'pagina1',
loadChildren: () => import('./pagina1.module').then(m => m.Pagina1Module),
},
...
]
Com a nova opção poderia ser reescrito da seguinte forma:
pagina1.component.ts
@Component({
template: 'Olá!',
standalone: true
})
export class Pagina1Component{}
app-routing.module.ts
const routes: Routes = [
...,
{
path: 'pagina1',
loadComponent: () => import('./pagina1.component').then(m => m.Pagina1Component),
},
...
]
Note que não existiria mais a necessidade de criar um sub-roteamento, ficando muito mais limpo.
Estou ansioso por essas mudanças. Acredito que elas vão facilitar o aprendizado de novos adeptos e possibilitar uma solução muito mais limpa para as aplicações Angular.
Infelizmente acho que essa novidade virá apenas em meados de abril/maio de 2022, com o Angular 14, considerando o calendário semestral de lançamentos major.