Codeblox Blog

Proposta de Componentes sem Módulos no Angular

Guilherme
13/10/2021

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.

O padrão SCAM

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).

Standalone Components

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;
}

Rotas com Lazy Loading

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.

angular
frontend


Guilherme M. Abdo
Guilherme M. Abdo
Desenvolvedor fullstack .Net/Angular/Azure/GitHub