Abordagem de validação baseada em modelo de dados de formulários angulares

Esta postagem do blog acompanha o código da diretiva ndValidation publicado no Github

Fundo:

Enquanto trabalhava em um projeto novo de pilha MEAN para um cliente que planeja basear vários produtos na mesma infraestrutura, pensei muito em diferentes áreas da organização de código, padrões recorrentes e ferramentas.

Uma das minhas conclusões foi que a validação do formulário exige muito esforço do lado dos desenvolvedores. Embora a “maneira angular” de fazer as coisas seja declarada e fácil de entender enquanto se desliza sobre o HTML, é difícil de manter, pois significa que em cada visão onde um modelo é editado, o desenvolvedor precisa repetir a complicada matriz de diretivas de validação e elementos de mensagem de validação com a lógica de exibição relacionada e, claro, o texto da mensagem que se repete para cada campo. Isso parecia errado para mim.

Usar mensagens ng do angular 1.3 (que no momento da escrita está em estágio beta) simplifica um pouco as coisas, mas não tanto quanto eu acho que é necessário.

Inspiração

Como estou vindo do background de PHP, a classe de modelo de dados parecia ser o lugar certo para definir as necessidades de validação de cada propriedade, é assim que funciona nos frameworks PHP que usei.

Depois de ler o artigo de Ben Teese sobre Modelos de dados ricos em Angular JS e outros como o artigo da Nan-Software sobre modelos de dados angulares, acostumei-me a manter métodos e valores de propriedade de inicialização na fábrica do modelo em vez do controlador.

Mas faltava uma peça ao colocar regras de validação no modelo – a diretiva que irá procurá-las e aplicá-las em cada entrada.
A ideia inicial de como fazer isso me ocorreu enquanto eu brincava com o projeto ousado e abrangente de ngActiveRecord, que vai mais longe do que eu achei necessário

PLANO

Comecei a definir o que queria alcançar:

Um modelo de dados carregará sua configuração de validação em seu protótipo, uma única diretiva no elemento de entrada verificará o modelo para a configuração de validação e aplicará a lógica para regras existentes usando a infraestrutura angular regular de validação de formulário – métodos de controlador de modelo ng que se aplicarão relevantes e afetam a validade da forma global, também esta diretiva criará os elementos relevantes das mensagens do usuário com a lógica de exibição complementar e o texto da mensagem relevante.

Outra consideração que fiz é que esta diretiva não deve afetar o desempenho e não deve criar $ watchers desnecessários ou escopos redundantes.

Eu sabia que a maioria das validações que eu precisava são incorporadas ao angular, mas alguns outros recursos que eu precisava implementar, como funções personalizadas (para verificação de validade do lado do servidor) exigiam algum código de validação personalizado, uma vez que eu já estava familiarizado com métodos de controlador de modelo ng e interface do usuário -utils validate diretiva para código de validação personalizado Eu sabia que poderia atender a isso facilmente.

ESTRUTURA

Minha solução de infraestrutura de validação é construída pelo seguinte:

  • ndValidation – uma diretiva que verifica o modelo (ou a fonte nomeada avaliada via atributo) para a configuração de validação e, se encontrada, recompila a diretiva com as diretivas de validação relevantes e o elemento validMessage relevante.
  • ndValidationService – fornece um objeto compartilhado para essas diretivas.
  • validationMessage – compila uma mensagem de validação padrão com o texto da mensagem traduzido que é acionado pelo estado ngModel
  • ngModel – diretiva adicionada ao ngModel que publica o estado de validade no ndValidationService compartilhado

Além do angular óbvio, não há dependências, mas eu recomendo adicionar o seguinte ao seu projeto:
angular-translate – para tradução de mensagens do usuário, pode ser facilmente desabilitado ou trocado por outra solução se necessário, embora eu recomende isso vivamente.
ui-utils validate – permite definir funções de validação personalizadas, mais informações no leiame.

o validationConfig é armazenado no protótipo do modelo usando, no nosso caso estávamos usando Restangular para REST, então eu o conectei usando a função Restangular.extendModel, mas pode ser feito de outra forma.

DICA

Uma coisa que devemos lembrar é que, para objetos baseados em entrada do usuário (novas entradas não existentes no banco de dados), o controlador instancia o escopo com o novo modelo de dados (de nossa fábrica) para que a configuração de validação esteja lá.
Então, em vez de ter no controlador:

$scope.newUser = {};

e na vista:

<input ng-model=’user.name’ required min-length=’5’ ng-pattern=’/[a-z]*/’/>

nós temos a fábrica:

angular.module(‘user’).factory(‘userModel’,function userFactory(data){

function User(){
this.name=data.name || ‘’;

}
User.prototype.$validationConfig = {
name
:[{required:true,message:’The name field is required’}]
}
return User;
});

e no controlador:

$scope.newUser = new UserModel();

e na vista:

<input ng-model=’user.name’ nd-validation/>