Ir para o conteúdo principal
WBC - GovBR-DS
Copiar página como Markdown para IA

Dados remotos e carregamento

Componentes visuais não fazem chamadas HTTP. A aplicação escuta os eventos de interação, controla debounce, autenticação, cache e cancelamento e devolve os dados por propriedades, slots ou conteúdo projetado.

Busca em br-select​

Use filterable e search-mode="remote". O evento brSelectSearch contém a consulta atual em event.detail.query, inclusive quando ela é limpa.

let requestId = 0;
let controller;

select.addEventListener('brSelectSearch', async ({ detail: { query } }) => {
controller?.abort();
const currentRequest = ++requestId;
controller = new AbortController();
select.loading = true;

try {
const url = new URL('/api/options', location.origin);
url.searchParams.set('q', query.trim());
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('Não foi possível carregar as opções.');
const result = await response.json();
if (currentRequest === requestId) await select.setOptions(result.items);
} catch (error) {
if (!(error instanceof DOMException && error.name === 'AbortError')) {
// Mostre uma mensagem role="alert" fora do componente.
}
} finally {
if (currentRequest === requestId) select.loading = false;
}
});

Defina o limite mínimo de caracteres na aplicação. Por exemplo, antes de consultar o serviço, limpe as opções quando a consulta tiver menos de dois caracteres. O consumidor também pode mapear a resposta para o formato { label, value } antes de chamar setOptions().

O componente preserva a seleção atual quando o item selecionado não aparece na janela de resultados seguinte. O consumidor deve mapear e validar o payload da API antes de passá-lo ao componente.

Angular standalone com FormControl​

[!IMPORTANT] A faixa de Angular suportada é a declarada nas peerDependencies do wrapper (veja compatibilidade). Em versões do Angular mais novas que a usada pelo Quickstart, valide compilação AOT, formulário e navegador antes de adotar o procedimento em produção.

Instale o wrapper e o Web Component na mesma versão. O npm instala também as dependências peer declaradas pelo pacote (@govbr-ds/core, @floating-ui/dom, date-fns e iconify-icon):

npm install @govbr-ds/webcomponents @govbr-ds/webcomponents-angular

Importe os estilos globais uma vez em src/styles.css:

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

Registre o HttpClient no app.config.ts:

import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()],
};

O componente standalone precisa importar três peças distintas:

  • BrSelect, que registra e expõe o Web Component;
  • SelectValueAccessor, que conecta value, change, estado desabilitado e toque ao FormControl;
  • FormValidityValidator, que encaminha a validade nativa do Web Component ao Angular Forms.
import { HttpClient } from '@angular/common/http';
import { Component, DestroyRef, ViewChild, inject, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject, catchError, debounceTime, distinctUntilChanged, finalize, map, of, switchMap } from 'rxjs';
import type { SelectOptionData, SelectSearchDetail } from '@govbr-ds/webcomponents';
import { BrSelect, FormValidityValidator, SelectValueAccessor } from '@govbr-ds/webcomponents-angular/standalone';

type OrgaoApi = {
id: string;
nome: string;
};

@Component({
selector: 'app-orgao-select',
standalone: true,
imports: [ReactiveFormsModule, BrSelect, SelectValueAccessor, FormValidityValidator],
template: `
<br-select
[formControl]="orgaoId"
label="Órgão"
placeholder="Digite ao menos dois caracteres"
searchMode="remote"
[filterable]="true"
[loading]="loading()"
required
(brSelectSearch)="onSearch($event)"
></br-select>

@if (errorMessage()) {
<p role="alert">{{ errorMessage() }}</p>
}
`,
})
export class OrgaoSelectComponent {
private readonly http = inject(HttpClient);
private readonly destroyRef = inject(DestroyRef);
private readonly queries = new Subject<string>();

@ViewChild(BrSelect) private select?: BrSelect;

readonly orgaoId = new FormControl('', {
nonNullable: true,
validators: [Validators.required],
});
readonly loading = signal(false);
readonly errorMessage = signal('');

constructor() {
this.queries
.pipe(
map((query) => query.trim()),
debounceTime(300),
distinctUntilChanged(),
switchMap((query) => {
this.errorMessage.set('');

if (query.length < 2) return of([] as SelectOptionData[]);

this.loading.set(true);

return this.http.get<OrgaoApi[]>('/api/orgaos', { params: { q: query } }).pipe(
map((items) =>
items.map((item) => ({
label: item.nome,
value: item.id,
})),
),
catchError(() => {
this.errorMessage.set('Não foi possível consultar os órgãos. Tente novamente.');
return of([] as SelectOptionData[]);
}),
finalize(() => this.loading.set(false)),
);
}),
takeUntilDestroyed(this.destroyRef),
)
.subscribe((options) => void this.select?.setOptions(options));
}

onSearch(event: CustomEvent<SelectSearchDetail>): void {
this.queries.next(event.detail.query);
}
}

O exemplo usa um FormControl isolado. Se o controle estiver dentro de um FormGroup, substitua [formControl] por formControlName="orgaoId" e declare o grupo no elemento <form [formGroup]="form">.

O switchMap cancela a inscrição da requisição anterior do HttpClient quando chega uma consulta nova. setOptions() atualiza a janela de resultados sem apagar uma seleção remota que esteja fora da resposta atual. A escolha do usuário chega a orgaoId.valueChanges; orgaoId.setValue(...), reset(), disable() e enable() percorrem o caminho inverso pelo value accessor. No modo múltiplo, adicione [multiple]="true" e use um FormControl<string[]> não anulável; o value accessor entrega a lista de valores selecionados.

Não instancie core.BRSelect, não use ngDefaultControl, não registre CUSTOM_ELEMENTS_SCHEMA e não chame defineCustomElements() manualmente neste fluxo. Também não misture WebcomponentsAngularModule.forRoot() com os proxies standalone no mesmo componente.

Se aparecer NG01203: No value accessor, confirme que SelectValueAccessor veio do subpath @govbr-ds/webcomponents-angular/standalone e está no array imports. Confirme também que wrapper e Web Components possuem a mesma versão:

npm ls @govbr-ds/webcomponents @govbr-ds/webcomponents-angular

br-pagination e br-table​

br-pagination já emite brPaginationPageChange e brPaginationPerPageChange. Defina loading enquanto busca a página; os controles ficam bloqueados e aria-busy="true" é aplicado.

Use controlled quando a página visível só puder mudar após uma resposta bem-sucedida. Nesse modo, os eventos comunicam page ou perPage, mas a aplicação confirma a mudança atribuindo current ou perPage.

br-table recebe loading enquanto suas linhas são atualizadas. As linhas anteriores permanecem visíveis para evitar mudança de layout; o novo conteúdo continua sendo fornecido pelos slots header e body.

Outros campos​

Validação remota em br-input e br-textarea​

Para validar disponibilidade, unicidade ou outra regra de domínio no servidor, atribua uma função assíncrona a validator. A função recebe o valor textual e retorna null quando válido ou uma mensagem quando inválido. O componente expõe aria-busy="true", o slot validation-loading e um evento de início e fim da validação. A validação ocorre no change ou quando a aplicação chama validate(); não ocorre a cada tecla.

input.validator = async (value) => {
const response = await fetch(`/api/usuarios/${encodeURIComponent(value)}`);
if (!response.ok) throw new Error('Falha ao consultar o usuário.');
return (await response.json()).available ? null : 'Valor já utilizado.';
};

const valid = await input.validate();

Use AbortController e um identificador de requisição na própria função quando a política da aplicação exigir cancelamento. O componente ignora o resultado de uma validação antiga, mas não cancela automaticamente o fetch. Consulte também as seções de validação do input e do textarea.

Upload assíncrono em br-upload​

br-upload aceita uploadHandler como propriedade JavaScript. O callback recebe file, signal e onProgress; endpoint, autenticação e formato da resposta continuam externos. Use brUploadStart, brUploadProgress, brUploadSuccess e brUploadError para atualizar a aplicação. Sem uploadHandler, o componente mantém apenas a seleção e a validação dos arquivos.

upload.uploadHandler = async ({ file, signal, onProgress }) => {
const body = new FormData();
body.append('arquivo', file);

// fetch não expõe progresso de upload; use um cliente que aceite onProgress
// quando a interface precisar exibir percentual real.
const response = await fetch('/api/arquivos', { method: 'POST', body, signal });
if (!response.ok) throw new Error('Falha no envio.');
};

Quando o cliente de transporte oferecer progresso, encaminhe-o por onProgress. cancelUpload() aborta o envio atual pelo AbortSignal.

br-list e br-dropdown são componentes de composição e também não assumem transporte ou estado de dados.

Comece aquiComponentesQuickstartsSobre o projetoRepositório GitLabVersõesDiscordGovBR-DSFAQWikiRedefinir cookies