Nesse post vamos aprender sobre mais um plugin do knockoutjs: knockout validation. Trata-se de um plugin de validação para trabalhar com o view model do KO. Devemos utilizar o próprio arquivo js do KO e o o arquivo do plugin que pode ser referenciado aqui: http://cdnjs.cloudflare.com/ajax/libs/knockout-validation/1.0.2/knockout.validation.min.js. O plugin faz uso de “extenders” do KO, para entender melhor para que server, e como implementar um customizado leia https://andrebtoe.com/2014/10/15/como-estender-um-observable-no-knockoutjs.

Veja um exemplo mais completo usando o plugin de validação http://jsfiddle.net/andrebtoe/yr508w0c/1

Vamos fazer um exemplo simples, primeiro devemos criar uma classe para ser utilizada como view model:

function ViewModel(){
    var self = this;

    self.Nome = ko.observable().extend({ required: true });
}

Acima definimos que a classe “ViewModel” possui uma propriedade observable, e utiliza um “extender” “required”.
Também devemos inicializar a validação da seguinte maneira:

ko.validation.init();
ko.applyBindings(new ViewModel());

Devemos chamar o método “ko.validation.init” antes do método do KO “ko.applyBindings”, para a validação funcionar corretamente.
Vamos adicionar o html para ver a validação funcionando.

<form>
    <input type="text" data-bind="value: Nome" />
    <input type="submit" value="Enviar" />
</form>

Acima temos apenas uma tag input que vincula a propriedade “Nome”, através do “data-bind”.

Veja funcionando http://jsfiddle.net/andrebtoe/zqeLxdLz/1.
Se você informar alguns espaços no campo, perceberá que uma mensagem será inserida ao lado (não submeta os dados do formulário, explicarei como implementar o submit mais abaixo).

validacaoKO1

Mensagem
Como podemos visualizar até o momento, a mensagem de “required” é “This field is required”, vamos mudar a mensagem utilizando os parâmetros da validação.

function ViewModel(){
    var self = this;

    self.Nome = ko.observable().extend({ required: {
        params: true,
        message: "Preenchimento obrigatório"
    }});
}

Acima trocamos o valor “true” por um objeto que tem “params” e “message”, em alguns caso o valor de “params” é utilizado como parâmetro de validação, como por exemplo o tipo de validação “min”. O atributo “message” é para definir o texto de validação que deverá ficar visível.
http://jsfiddle.net/andrebtoe/hq8bmwfu

Local da mensagem
Podemos definir o local onde mensagem de erro deve aparecer, para isso devemos criar um elemento html e atrelar a validação, o html deve ficar assim:

<form>
    <span class="error" data-bind="validationMessage: Nome"></span>
    <input type="text" data-bind="value: Nome" />
    <input type="submit" value="Enviar" />
</form>

Acima definimos que uma tag span deve ficar responsável por mostra a mensagem de erro da propriedade “Nome”, utilizando o “bindingHandler” “validationMessage”. Isso funciona, porem tem um efeito colateral indesejado (mas nem sempre): “A mensagem de erro é exibida 2 vezes, antes e depois da tag input”, veja no exemplo http://jsfiddle.net/andrebtoe/mpkcc4br.

Para ressorvermos esse problema, temos que informar que a mensagem não deve ser exibida em um dos casos

<form>
    <span class="error" data-bind="validationMessage: Nome"></span>
    <div data-bind="validationOptions: { insertMessages: false, decorateInputElement: false}">
        <input type="text" data-bind="value: Nome" />
    </div>
    <input type="submit" value="Enviar" />
</form>

Colocamos o input envolto de uma tag div, e nela informamos algumas opções de validação, assim todas as validações nesse contexto não devem serem exibidas http://jsfiddle.net/andrebtoe/0aphLbmn.

Submeter os dados
Como citei acima, caso o formulário fosse submetido a validação não funcionaria, pois bem, devemos definir uma propriedade que ficará responsável pela validações, assim saberemos quando podemos submeter ou não. Vamos adicionar a propriedade “Erros” e adicionar um método para validar “EnviarDados”.

function ViewModel(){
    var self = this;

    self.Nome = ko.observable().extend({ required: {
        params: true,
        message: "Preenchimento obrigatório"
    }});

    self.Erros = ko.validation.group(self);

    self.EnviarDados = function(){
        if(self.Erros().length == 0)
            alert("Formulário válido");
        else
            self.Erros.showAllMessages();
    }
}

Adicionamos a propriedade “Erros” que recebe o retorno o método “ko.validation.group”, com as definições da validação da instancia (this), ele basicamente disponibiliza a quantidade de erros que a instancia possui. E tambem adicionamos o método “EnviarDados”, que por sua vez, vefica a quantidade de erros, caso seja zero, um “alert” é executado, do contrário, é executado o método “showAllMessages”, para mostrar todas as mensagens de erro http://jsfiddle.net/andrebtoe/jgxxku75.

Validação condicional
É possível adicionar uma validação condicional, por exemplo, uma determinada propriedade só deve ser obrigatório caso a propriedade possua valor x. Para o exemplo vamos adicionar uma propriedade no view model, para controlar com um booleano, se o campo é obrigatório ou não, e vamos colocar uma condição na validação de “Nome”, para definir quando validar, utilizando o atributo “onlyIf”

function ViewModel(){
    var self = this;

    self.PossuiNome = ko.observable(true);

    self.Nome = ko.observable().extend({ required: {
        params: true,
        message: "Preenchimento obrigatório",
        onlyIf: function(){
            return self.PossuiNome();
        }
    }});

    self.Erros = ko.validation.group(self);

    self.EnviarDados = function(){
        if(self.Erros().length == 0)
            alert("Formulário válido");
        else
            self.Erros.showAllMessages();
    }
}

Acimas adicionamos a propriedade “PossuiNome”, que é observable e possui o valor “true” como padrão. E adicionamos o atributo “onlyIf”, na validação “required” de “Nome”. O método sempre deve retornar um valor booleano, parta indicar se deve validar ou não, no nosso caso, estamos retornando apenas o valor da propriedade “PossuiNome”, não houve necessidade de verificar nada. E modificamos o html

<form data-bind="submit: EnviarDados">
    <label>
        Possui nome
        <input type="checkbox" data-bind="checked: PossuiNome" />
    </label>
    <br />
    <label>
        Nome:
        <input type="text" data-bind="value: Nome" />
    </label>
    <input type="submit" value="Enviar" />
</form>

Adicionamos a tag input do tipo “checkbox”, para atrelar com a propriedade “PossuiNome”, utilizando o “bindingHandler” “checked”. http://jsfiddle.net/andrebtoe/00583t68.

Outros tipos de validação
Até o momento vimos apenas o tipo “required”, o knockout validation disponibiliza alguns tipos:

Validação Descrição Parametros Exemplo
Required Define obrigatoriedade para propriedade.
ko.observable().extender({ requird: true });
Min Define um número minimo para ser utilizado. Número minimo
ko.observable().extender({ min: 4 });
Max Define um número máximo para ser utilizado. Número máximo
ko.observable().extender({ max: 4 });
MinLength Define um número de caracteres minimo para ser utilizado. Número minimo
ko.observable().extender({ minLength: 4 });
MaxLength Define um número de caracteres máximo para ser utilizado. Número máximo
ko.observable().extender({ maxLength: 4 });
Pattern Define uma expressão regular para ser utilizada. Expressão regular
ko.observable().extender({ pattern: /^[0-9]$/ });
Date Define uma data válida para ser utilizada.
ko.observable().extender({ date: true });
Email Define um e-mail válido para ser utilizado.
ko.observable().extender({ email: true });
Equal Define o valor que deve ser utilizado exatamente. Valor
ko.observable().extender({ equal: "Olá" });
NotEqual Define o valor que não deve ser utilizado exatamente. Valor
ko.observable().extender({ notEqual: "Olá" });
Number Define um valor numérico para ser utilizado.
ko.observable().extender({ number: true });

Podemos utilizar qualquer um dos tipos de validação da tabela acima.

Novo tipo de validação
Podemos criar um novo tipo a ser utilizado para validação. Vamos adicionar o tipo url

ko.validation.rules["url"] = {
    validator: function (val, required) {
        if (!val) {
            return !required
        }
        val = val.replace(/^\s+|\s+$/, '');
        return val.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.‌​\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[‌​6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1‌​,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00‌​a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u‌​00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i);
    },
    message: "URL inválida"
};

No código acima definimos o nome da validação “url”, e passamos um objeto com o parâmetro, onde temos o atributo “validator”, que recebe uma função com dois parâmetros, o primeiro retorna o valor da propriedade que está sendo validada, e o segundo o valor de “params”. Já na implementação, verificamos se o valor da propriedade está definido, caso não esteja, o segundo parâmetro é considerado para validar. Caso a propriedade possua um valor definido, validamos o valor com uma expressão regular. o Atributo “message” define a mensagem padrão da validação, que pode ser redefinida quando utilizada http://jsfiddle.net/andrebtoe/u52dndz0.
Um tipo de validação que não é implementado pelo plugin, e seu uso é recorrente é o “remote”, vamos ver como adicionar.

ko.validation.rules["remote"] = {
    async: true,
    validator: function (val, parms, callback) {
        var defaults = {
            type: "POST",
            success: callback
        };

        var options = $.extend(defaults, parms);
        $.ajax(options);
        this.message = options.mens;
    },
    message: ""
};

Como a intenção da validação é executar um ajax (estamos utilizando o jQuery para esse fim), é necessário definir o atributo “async” como “true”, assim a requisição se torna assíncrona, para o plugin.

Personalizar validação
Podemos mudar algumas configurações padrões do plugin, para isso devemos chamar o método “ko.validation.configure”, antes do “ko.applyBindings”, temos:

ko.validation.configure({
    registerExtenders: true,
    messagesOnModified: true,
    decorateElementOnModified: true,
    errorElementClass: 'error',
    errorMessageClass: 'error',
    grouping: { deep: true, observable: true }
});

Os dois atribuitos de grouping tem o valor “false” como padrão.

Atributo Descrição
registerExtenders Define que as novas validações serão registradas automaticamente.
messagesOnModified Indica se as mensagens de validação são acionados apenas quando as propriedades observables são modificadas ou em todos os momentos.
decorateElementOnModified Indica se deve adicionar uma classe (no nosso casso: ‘error’, isso pode ser visto no atributo “errorElementClass”) ao input atrelado.
errorElementClass Classe do css que deve ser atribuída ao elemento atrelado a propriedade observable, caso o atributo “decorateElementOnModified” seja “true”.
errorMessageClass Classe do css que deve ser definida a mensagem de erro.
grouping É possível determinar se a validação deve ser feita apenas em um nível, ou validar as propriedade recursivamente com:
* deep: Valida em profundidade, todos os sub níveis de instancia do view model
* observable: Indica que as propriedade deve ser simples ou computadas.

Quando se utiliza o método “ko.validation.configure”, não é necessário chamar “ko.validation.init”, Veja um exemplo http://jsfiddle.net/andrebtoe/0t2p0hyL

Até a próxima pessoal!

Publicidade