Ir para o conteúdo principal

Angular – @govbr-ds/webcomponents-angular

npm (next)

Este é um wrapper Angular que encapsula os Web Components GovBR-DS, habilita NG_VALUE_ACCESSORS e permite a vinculação de eventos de entrada diretamente a um value accessor, proporcionando uma integração perfeita no fluxo de dados bidirecional do Angular.

Por que usar este wrapper? 🤔

  • Desacoplamento da detecção de mudanças dos elementos Web.
  • Conversão de eventos para observáveis RxJS, alinhado ao @Output().
  • Control Value Accessors para integração com Reactive Forms e ngModel.

[!TIP] Para mais detalhes, consulte a documentação oficial do Stencil.

Instalação 📦

Instale o wrapper e suas dependências:

npm install @govbr-ds/webcomponents-angular
# ou
pnpm add @govbr-ds/webcomponents-angular
# ou
yarn add @govbr-ds/webcomponents-angular

peerDependencies

peerDependencies são pacotes que este wrapper não instala automaticamente — o seu projeto precisa tê-los instalados.

Observe que algumas peerDependencies podem ter suas próprias peerDependencies que também precisam ser atendidas. Consulte a documentação de cada pacote para garantir que todas as dependências necessárias estejam presentes.

Por que existem: Garantem que o Angular CLI e o wrapper compartilhem a mesma instância do Angular e dos Web Components. Versões duplicadas podem causar falhas na compilação ou erros em tempo de execução.

O que isso implica: Se as peers não estiverem instaladas ou forem de versão incompatível, o npm emitirá avisos e o pacote pode não funcionar corretamente.

As peers declaradas neste pacote são:

PacoteVersão mínima
@angular/core>=16.0.0
@angular/common>=16.0.0
@govbr-ds/webcomponents^2

Nota importante: pnpm e tree-shaking

Se ao consumir estes pacotes você notar que o bundler não está removendo código não utilizado (tree‑shaking), pode haver uma incompatibilidade com o layout padrão do pnpm.

Solução rápida (opcional, somente se precisar): crie um arquivo .npmrc na raiz do seu projeto com:

node-linker=hoisted

Por que isso ajuda: por padrão, o pnpm organiza as dependências em pastas isoladas com symlinks. Alguns bundlers/otimizadores se baseiam na estrutura de node_modules e no campo sideEffects para decidir o que pode ser eliminado. O layout hoisted aproxima o formato “achatado” (similar ao npm/yarn), facilitando essa análise e, em muitos casos, restaurando o tree‑shaking.

Observações:

  • Use apenas se o tree‑shaking realmente não estiver funcionando.
  • Pode aumentar o uso de disco e alterar a resolução de dependências do seu projeto.

Uso 📚

Fontes e ícones

Inclua os estilos-base no Angular (via angular.json ou CSS global):

// angular.json (trecho)
{
"projects": {
"app": {
"architect": {
"build": {
"options": {
"styles": ["node_modules/@govbr-ds/core/dist/core-tokens.min.css"]
}
}
}
}
}
}

Ou importe no seu stylesheet global:

@import '~@govbr-ds/core/dist/core-tokens.min.css';

Angular com módulos

// app.module.ts
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { WebcomponentsAngularModule } from '@govbr-ds/webcomponents-angular'
import { AppComponent } from './app.component'

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule, WebcomponentsAngularModule.forRoot()],
bootstrap: [AppComponent],
})
export class AppModule {}

Angular standalone

// app.component.ts
import { Component } from '@angular/core'
import { BrButton } from '@govbr-ds/webcomponents-angular/standalone'

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
standalone: true,
imports: [BrButton],
})
export class AppComponent {}
<!-- app.component.html -->
<br-button>Lorem ipsum</br-button>

Uso com NgModel e binding

Para habilitar ngModel e 2-way binding, adicione FormsModule e o atributo ngDefaultControl no componente de formulário.

[!CAUTION] Em algumas versões pode ser necessário desativar aot. Veja: issue #317

<br-checkbox
name="userTermsConditions"
[(ngModel)]="termsConditions"
(brCheckedChange)="onTermsConditionsChange()"
ngDefaultControl
>
Concordo com os Termos e Condições
</br-checkbox>
// app.component.ts
import { Component } from '@angular/core'

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
termsConditions = true
onTermsConditionsChange() {
console.log('O valor mudou!', this.termsConditions)
}
}

Desenvolvimento 👨‍💻

Estrutura do projeto

├── 📁 src
│ ├── 📁 stencil-generated
│ ├── 📄 angular-webcomponents.module.ts
│ └── 📄 index.ts
├── 📁 standalone
│ └── 📁 src
│ ├── 📁 stencil-generated
│ └── 📄 index.ts
└── 📁 scripts

[!WARNING] Tudo dentro de stencil-generated é sobrescrito ao gerar o build de Web Components.

Scripts/Build

Gere os Web Components antes de compilar o wrapper:

nx build webcomponents
nx build angular

Formatos do build 📦

A tarefa nx build angular compila o wrapper com ng-packagr e gera a saída em dist/angular/. Abaixo estão os artefatos produzidos e quando utilizá-los.

Estrutura do dist/angular/

dist/angular/
├── esm2022/ ← Módulos ESM (Angular Ivy)
│ ├── index.js
│ └── stencil-generated/
├── fesm2022/ ← Flat ESM bundle (otimizado para bundlers)
│ └── govbr-ds-webcomponents-angular.mjs
├── standalone/ ← Componentes standalone (sem NgModule)
│ ├── esm2022/
│ └── fesm2022/
├── index.d.ts ← Tipos TypeScript (entrada principal)
├── package.json ← Campos exports/main/module para resolução
└── README.md

Quando usar cada formato

ArtefatoQuando usarObservações
fesm2022/Aplicações Angular 16+ (padrão)Resolvido automaticamente pelo Angular CLI via campo exports
esm2022/Toolchains que necessitam de módulos individuaisPermite tree-shaking granular por arquivo
standalone/Aplicações Angular standalone (sem NgModule)Importação via @govbr-ds/webcomponents-angular/standalone
index.d.tsAutocomplete e tipagem TypeScriptTipos para diretivas, value accessors e módulo

NgModule vs Standalone

O pacote disponibiliza duas variantes de uso:

  • NgModule (entrada principal): use WebcomponentsAngularModule.forRoot() no imports do módulo.
  • Standalone (subpath /standalone): importe componentes individuais diretamente.
// NgModule
import { WebcomponentsAngularModule } from '@govbr-ds/webcomponents-angular'

@NgModule({
imports: [WebcomponentsAngularModule.forRoot()],
})
export class AppModule {}

// Standalone
import { BrButton } from '@govbr-ds/webcomponents-angular/standalone'

@Component({
standalone: true,
imports: [BrButton],
})
export class AppComponent {}

[!NOTE] Ambas as variantes dependem de @govbr-ds/webcomponents e @govbr-ds/core instalados.

Documentações Complementares 📖

SSR (Server-Side Rendering)

Web Components dependem de APIs do navegador (DOM, window, customElements). Para projetos Angular Universal / SSR:

  1. Proteja imports com verificação isPlatformBrowser:
import { isPlatformBrowser, PLATFORM_ID } from '@angular/common'
import { inject } from '@angular/core'

const isBrowser = isPlatformBrowser(inject(PLATFORM_ID))
  1. Use afterNextRender() (Angular 16+) para lógica que depende do DOM:
afterNextRender(() => {
// Código que acessa o DOM
})
  1. Em angular.json, importe os estilos CSS condicionalmente para evitar warnings no servidor.

Referências Complementares📖

Contribuindo 🤝

Reportar Bugs/Problemas 🐛

Abra uma issue: gitlab.com/.../issues/new

Commits 📝

Padrões de branches e commits: gov.br/ds/wiki

Precisa de ajuda? 🆘

Créditos 🎉

Desenvolvido pelo SERPRO com a comunidade.