Como posso criar um componente personalizado que funcione como uma <input>
tag nativa ? Quero fazer com que meu controle de formulário personalizado seja capaz de oferecer suporte a ngControl, ngForm, [(ngModel)].
Pelo que entendi, preciso implementar algumas interfaces para fazer meu próprio controle de formulário funcionar como o nativo.
Além disso, parece que a diretiva ngForm se vincula apenas a <input>
tag, certo? Como posso lidar com isso?
Deixe-me explicar por que preciso disso. Quero agrupar vários elementos de entrada para torná-los capazes de trabalhar juntos como uma única entrada. Existe outra maneira de lidar com isso? Mais uma vez: quero fazer esse controle igualzinho ao nativo. Validação, ngForm, ngModel ligação bidirecional e outros.
ps: Eu uso o Typescript.
fonte
Respostas:
Na verdade, há duas coisas a serem implementadas:
ngModel
si mesmaControlValueAccessor
que irá implementar a ponte entre este componente engModel
/ngControl
Vamos dar uma amostra. Quero implementar um componente que gerencia uma lista de tags para uma empresa. O componente permitirá adicionar e remover tags. Quero adicionar uma validação para garantir que a lista de tags não está vazia. Vou defini-lo em meu componente conforme descrito abaixo:
O
TagsComponent
componente define a lógica para adicionar e remover elementos datags
lista.Como você pode ver, não há nenhuma entrada neste componente, mas uma
setValue
(o nome não é importante aqui). Nós o usaremos posteriormente para fornecer o valor dengModel
para o componente. Este componente define um evento para notificar quando o estado do componente (a lista de tags) é atualizado.Vamos implementar agora o link entre este componente e
ngModel
/ngControl
. Isso corresponde a uma diretiva que implementa aControlValueAccessor
interface. Um provedor deve ser definido para este acessador de valor em relação aoNG_VALUE_ACCESSOR
token (não se esqueça de usarforwardRef
pois a diretiva é definida depois).A diretiva anexará um ouvinte de evento no
tagsChange
evento do host (ou seja, o componente ao qual a diretiva está anexada, ou seja, oTagsComponent
). OonChange
método será chamado quando o evento ocorrer. Este método corresponde ao registrado pelo Angular2. Desta forma, ele estará ciente das alterações e atualizações de acordo com o controle de formulário associado.O
writeValue
é chamado quando o valor vinculado angForm
é atualizado. Depois de injetar o componente anexado (isto é, TagsComponent), poderemos chamá-lo para passar este valor (veja osetValue
método anterior ).Não se esqueça de fornecer
CUSTOM_VALUE_ACCESSOR
nas ligações da diretiva.Aqui está o código completo do custom
ControlValueAccessor
:Desta forma, quando eu removo todos os
tags
da empresa, ovalid
atributo docompanyForm.controls.tags
controle passafalse
a ser automático.Consulte este artigo (seção "Componente compatível com NgModel") para obter mais detalhes:
fonte
<textfield>
,<dropdown>
? É esta forma "angular"?Não entendo porque todos os exemplos que encontro na internet têm que ser tão complicados. Ao explicar um novo conceito, acho que é sempre melhor ter o exemplo mais simples e funcional possível. Eu destilei um pouco:
HTML para formulário externo usando componente que implementa ngModel:
Componente independente (sem classe de 'acessador' separada - talvez eu esteja perdendo o ponto):
Na verdade, acabei de abstrair todas essas coisas para uma classe abstrata que agora estendo com todos os componentes de que preciso para usar o ngModel. Para mim, isso é uma tonelada de código indireto e clichê que posso dispensar.
Editar: Aqui está:
Este é um componente que o usa: (TS):
HTML:
fonte
@angular/forms
importações, basta atualizar:import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
CORE_DIRECTIVES
e adicioná-los ao,@Component
pois eles são fornecidos por padrão desde o Angular2 final. No entanto, de acordo com meu IDE, "Construtores para classes derivadas devem conter uma 'super' chamada.", Então eu tive que adicionarsuper();
ao construtor do meu componente.Há um exemplo neste link para a versão RC5: http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel
Podemos então usar esse controle personalizado da seguinte maneira:
fonte
O exemplo de Thierry é útil. Aqui estão as importações que são necessárias para o TagsValueAccessor rodar ...
fonte
Eu escrevi uma biblioteca que ajuda a reduzir alguns clichê para este caso:
s-ng-utils
. Algumas das outras respostas são exemplos de agrupamento de um único controle de formulário. Usandos-ng-utils
que pode ser feito muito simplesmente usandoWrappedFormControlSuperclass
:Em sua postagem, você menciona que deseja agrupar vários controles de formulário em um único componente. Aqui está um exemplo completo de como fazer isso com
FormControlSuperclass
.Então você pode usar
<app-location>
com[(ngModel)]
,[formControl]
, validadores customizados - tudo o que você pode fazer com os controles suportes angulares fora da caixa.fonte
Você também pode resolver isso com uma diretiva @ViewChild. Isso dá ao pai acesso total a todas as variáveis de membro e funções de um filho injetado.
Veja: Como acessar os campos de entrada do componente de formulário injetado
fonte
Por que criar um novo acessador de valor quando você pode usar o ngModel interno. Sempre que você está criando um componente personalizado que tem uma entrada [ngModel] nele, já estamos instanciando um ControlValueAccessor. E esse é o acessor de que precisamos.
modelo:
Componente:
Use como:
fonte
innerNgModel
é definido emngAfterViewInit
Isso é muito fácil de fazer
ControlValueAccessor
NG_VALUE_ACCESSOR
.Você pode ler este artigo para criar um campo personalizado simples Crie um componente de campo de entrada personalizado com Angular
fonte