Se você estiver trabalhando em um projeto complexo, provavelmente vai precisar criar um campo de formulário personalizado.
Existem alguns artigos que explicam como fazer, mas também é importante ter uma visão sobre o papel desse componente na arquitetura Angular.
Afinal, quando você lida com mecanismos mais complexos, é normal precisar entender o seu funcionamento…
E não apenas seguir um passo a passo de implementação.
Por isso, se quiser saber não apenas como implementar, mas também o “por quê”… esse artigo é para você.
Comece pelo FormControl
Se você já trabalhou com forms no Angular, então deve conhecer com o FormControl…
Caso não conheça, é um objeto que rastreia o valor e o status de validação de um campo de formulário (ou seja, elementos como input, textarea, select…)

É importante entender que, quando você trabalha com formulários, objetos de FormControl sempre são criados. Independente de você usar template driven ou reactive forms.
Com a abordagem reativa, você mesmo cria e usa o FormControl, e a diretiva formControlName, para vinculá-lo a um campo de formulário:
@Component({
selector: 'app-reactive-form',
template: `
<form [formGroup]="formulario">
<input type="text" formControlName="nome">
<input type="email" formControlName="email">
</form>
`
})
export class ReactiveFormComponent implements OnInit {
formulario: FormGroup;
ngOnInit() {
this.formulario = new FormGroup({
nome: new FormControl(null), <---------------- Aqui
email: new FormControl(null) <---------------- E aqui
}
}
}Mas, se você usar a abordagem baseada em template, o FormControl é criado implicitamente pela diretiva ngModel:
@Directive({
selector: '[ngModel]',
...
})
export class NgModel ... {
_control = new FormControl(); <---------------- AquiPerceba que o FormControl interage com um campo de formulário nativo. Como por exemplo: input ou textarea.
Porém você também pode usar em campos de formulário personalizados…
O que é campo de formulário personalizado?
Em vez de um campo de formulário nativo, você também pode criar um campo personalizado…
Sendo que os campos personalizados também interagem com um objeto FormControl.
Como por exemplo, um componente Angular atuando como input:
<form [formGroup]="formulario">
...
<ng-counter-input formControlName="contador"></ng-counter-input>
</form>O que é muito útil, pois o número de elementos nativos é limitado…
Mas para isso, o Angular precisa de um mecanismo genérico que fica entre o FormControl e o campo do formulário (seja ele nativo ou personalizado).
É aqui que o objeto ControlValueAccessor entra em jogo.
Entenda o objeto ControlValueAccessor
O ControlValueAccessor é o objeto que fica entre o Angular FormControl e o campo do formulário… A fim de sincronizar os 2.
Portanto ele atua como uma ponte entre o Angular forms API e o elemento no DOM.

Qualquer componente ou diretiva pode ser transformado em ControlValueAccessor, basta:
- Se registrar como um provider
NG_VALUE_ACCESSOR; - E implementar a interface
ControlValueAccessor.
O Angular usa o NG_VALUE_ACCESSOR para configurar a sincronização com o FormControl.
Geralmente é a classe do componente ou diretiva que registra o provider…
@Component({
selector: 'ng-counter-input',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: NgCounterInputComponent,
multi: true
}]
...
})
export class NgCounterInputComponent {...}Note que definimos o provider direto no descritor do componente. Além disso, não podemos ter mais de um accessor definido por elemento.
Depois disso você precisa implementar a interface:
@Component({
selector: 'ng-counter-input',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: NgCounterInputComponent,
multi: true
}]
...
})
export class NgCounterInputComponent implements ControlValueAccessor {...} <---------------- AquiPronto, agora temos um campo de formulário personalizado?
Não! Ainda precisamos codificar seu comportamento…
Como usar o ControlValueAccessor?

A interface ControlValueAccessor define 2 métodos importantes:
writeValue;- E
registerOnChange.
interface ControlValueAccessor {
writeValue(obj: any): void
registerOnChange(fn: any): void
...
// Métodos menos importantes aqui
}O método writeValue é usado para definir o valor do campo.
Já o registerOnChange serve para registrar uma função que deve ser acionada toda vez que o campo for atualizado… Assim ele repassa esse valor ao objeto FormControl.
Então, na essência, teríamos um código parecido com esse:
...
export class NgCounterInputComponent implements ControlValueAccessor {
value: number;
onChange: any;
writeValue(value: number): void {
this.value = value;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
...
}Veja também esse diagrama que ilustra a interação:

Exemplo prático de uso: <ng-counter-input>

Por último, vamos ver um exemplo real de input personalizado.
A ideia é criar um contador numérico com 2 botões: + e -.
@Component({
selector: 'ng-counter-input',
template: `
<div>
<label>{{ value }}</labe>
<button> + </button>
<button> - </button>
</div>
`
})
export class NgCounterInputComponent {...}Seu comportamento é simples:
- Quando apertar no botão
+, deve somar 1 ao valor do input; - E quando apertar no botão
-, deve subtrair 1.
Em primeiro lugar, você deve configurar o ControlValueAccessor como vimos nos tópicos anteriores:
@Component({
selector: 'ng-counter-input',
template: `
<div>
<label>{{ value }}</labe>
<button> + </button>
<button> - </button>
</div>
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: NgCounterInputComponent,
multi: true
}]
})
export class NgCounterInputComponent implements ControlValueAccessor {...}Depois escrever as funções writeValue e registerOnChange:
...
export class NgCounterInputComponent implements ControlValueAccessor {
value: number;
onChange: any;
writeValue(value: number): void {
this.value = value;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
}Porém tem uma pegadinha…
Nesse projeto o valor mínimo do input é zero, então devemos garantir isso ao receber o valor na função writeValue:
...
export class NgCounterInputComponent implements ControlValueAccessor {
value: number;
onChange: any;
writeValue(value: number): void {
if(value < 0) {
this.value = 0;
} else {
this.value = value;
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
}Terminamos? Ainda não… precisamos escrever o comportamento dos botões…
E para isso vamos criar 2 funções:
increment:para somar mais 1;decrement:para diminuir 1;
@Component({
selector: 'ng-counter-input',
template: `
<div>
<label>{{ value }}</labe>
<button (click)="increment()"> + </button>
<button (click)="decrement()"> - </button>
</div>
`,
...
})
export class NgCounterInputComponent implements ControlValueAccessor {
value: number;
onChange: any;
increment(): void {
this.value++;
this.onChange(this.value);
}
decrement(): void {
if(this.value > 0) {
this.value--;
this.onChange(this.value);
}
}
...
}Perceba que as 2 funções fazem uso do onChange para avisar ao FormControl que houve uma mudança.
Além disso, a função decrement implementa nossa regra de negócio onde o valor não pode ser menor que zero.
Agora sim nosso input personalizado está completo! Veja como ficou o código abaixo:
@Component({
selector: 'ng-counter-input',
template: `
<div>
<label>{{ value }}</labe>
<button (click)="increment()"> + </button>
<button (click)="decrement()"> - </button>
</div>
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: NgCounterInputComponent,
multi: true
}]
})
export class NgCounterInputComponent implements ControlValueAccessor {
value: number;
onChange: any;
increment(): void {
this.value++;
this.onChange(this.value);
}
decrement(): void {
if(this.value > 0) {
this.value--;
this.onChange(this.value);
}
}
writeValue(value: number): void {
if(value < 0) {
this.value = 0;
} else {
this.value = value;
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
}Conclusão
Claro que não abordamos tudo nesse único artigo, há mais funções disponíveis para você usar…
Como por exemplo, o método registerOnTouched, que indicar quando um usuário interage com o input.
Caso queira se aprofundar nesse mecanismo, recomendo que dê uma olhada na documentação aqui.
E se você gostou desse artigo, ou ficou com alguma dúvida, deixe seu comentário abaixo:

{ Angular Signals } Como deixar seu código mais reativo
![[Infográfico] 28 Melhores Extensões do Visual Studio Code para Angular](https://warcontent.com/wp-content/uploads/2020/07/extensoes-visual-studio-code-angular-1-556x313.png)
[Infográfico] 28 Melhores Extensões do Visual Studio Code para Angular

{ Angular LifeCycle Hooks } Controle sua Aplicação como um Mestre Jedi
![[Infográfico] Angular LifeCycle: O Guia Rápido de Referências do Jovem Padawan](https://warcontent.com/wp-content/uploads/2020/07/guia-angular-lifecycle-hooks-1-v4-556x313.png)
[Infográfico] Angular LifeCycle: O Guia Rápido de Referências do Jovem Padawan
Hey,
o que você achou deste conteúdo? Conte nos comentários.





![[Infográfico] Angular CLI: O Guia Rápido de Referências](https://warcontent.com/wp-content/uploads/2020/05/guia-angular-cli-0-556x313.png)
![[Infográfico] Estrutura de Arquivos de um Projeto Angular - O Guia Rápido](https://warcontent.com/wp-content/uploads/2020/05/angular-cli-1-556x313.png)




Quando precisei aprender a fazer um input personalizado esse artigo do indepth me ajudou bastante, então decidi fazer um resumo em português baseado nele. Ele lida de maneira mais aprofundada, recomendo muito a leitura. 😉