O knockoutjs nos permite criar “Bind handlers”, o framework disponibiliza alguns implementados como: click, value, text, etc. Desta forma podemos interagir com os elementos DOM, assim temos uma forma de encapsular comportamentos e reutilizar. Vamos começar com um exemplo simples.
Vamos imaginar que precisamos mostrar e esconder um elemento (esse binding ja existe no KO, porem julguei que seria de fácil entendimento; vou dar mais exemplos abaixo), então um simples “display” com “block” ou “none” resolveria carto?
Sim, resolveria, mas temos que criar isso de forma abstrata, ou seja, que poderá ser reutilizado. Devemos evitar um binding que “fique amarrado” a elementos.

ko.bindingHandlers.isVisible = {
    init: function (element, valueAccessor) {
        var isMostrar = ko.unwrap(valueAccessor());
        $(element).css("display", isMostrar ? "block" : "none");
    }
}

Acima temos na primeira linha, a assinatura do binding com o nome “isVisible”. Passamos um objeto com o atributo “init”, que recebe uma função com alguns parâmetros, existem mais que dois, que irei abordar mais abaixo, por hora:
element: O KO retornar o elemento ao qual foi atrelado.
valueAccessor: Propriedade observable (não é requisito ser um observable, também pode ser um dado primitivo) ao qual foi atrelada.

Na implementação de “init”, utilizamos o método “unwrap” do KO, para retornar o valor da propriedade, esse método é útil quando não se sabe se o a propriedade é um observable ou não, como se trata de um binding, é recomendado utilizar. Caso você não queira utilizar ko.unwrap, você pode obter o valor do observable chamando ele com “()”, ou seja, valueAccessor()() (são duas vezes mesmo), do contrário você obterá apenas o corpo da implementação da função observable. Mas voltando ao foco, a variável “isMostrar” obtêm o valor da propriedade informada, logo abaixo é feito uma verificação para definir se o elemento irá ficar visível ou não, repare que é utilizado um seletor jQuery, porem isso não é necessário, você pode utilizar outro framework ou javascript puro. Esse binding poderia ser utilizado dessa forma (imaginando que houvesse uma propriedade observable chamada “Aplicar”)

<div data-bind="isVisible: Aplicar"></div>

Caso o valor de “Aplicar” fosse “true” o elemento ficaria visível, do contrário não.

Outros parâmetros
Temos mais alguns parâmetros (alem do que foram visto acima) como:
allBindings: Podemos utilizar esse parâmetro para obter outro(s) objetos atrelados ao bindings, podemos deixar algumas opções disponíveis para habilitar um recurso por exemplo.
viewModel: Esse parâmetro retorna a instancia do seu viewModel, este item foi deprecado a partir da versão 3.x do KO, em vez disso utilize bindingContext.$data para acessar o dados de viewModel (próximo parâmetro).
bindingContext: Esse parâmetro disponibiliza os dados do viewModel, e da condições para acessar os dados ancestrais, ou seja, em outros níveis.

Vamos criar um exemplo que utilizaremos mais parâmetros que o primeiro http://jsfiddle.net/andrebtoe/b9mm2nhx/1

ko.bindingHandlers.textMoney = {
    init: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings();
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(2));
    }
};

Na primeira linha da implementação obtemos o valor de “allBindings”, assim poderemos utilizar as demais opções atrelada no elemento DOM, no caso estamos esperando apena um atributo: “moeda”. Logo abaixo capturamos o valor da propriedade, depois convertemos o valor para float, caso a conversão não tenha ocorrido com sucesso o valor deve ser 0 (zero), e por último mudamos o valor de text do elemento, com o valor passado para opção “moeda”, e o valor com notação de duas casas decimais.

<div data-bind="textMoney: preco, moeda: 'R$'"></div>

No jsfiddle criei uma instancia da classe ViewModel, com o observable “preco”.
Isso funciona como o esperado. Mas também podemos passar um objeto em vez de passar apenas um atributo com valor primitivo (alem de poder criar várias opções para o bindings). Vamos adicionar a possibilidade de controlar a quantidade de casas decimais http://jsfiddle.net/andrebtoe/gLakjgkc

ko.bindingHandlers.textMoney = {
    init: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings().textMoneyOptions;
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(opcoes.quantDec));
    }
};

Mudamos um pouco esse binding, primeiro mudamos o nome que atrela as opções para “textMoneyOptions”, e atribuimos o valor da mesma para variavel “opcoes”, assim podemos acessar os atributos de qualquer lugar dentro do método, no caso esperamos “moeda” e “quantDec”. Utilizaremos dessa forma:

<div data-bind="textMoney: preco, textMoneyOptions: {moeda: 'R$', quantDec: 3}"></div>

Acima definimos a propriedade observable que o binding “textMoney” utilizará, e suas opções devidas com “textMoneyOptions”.
Se atualizarmos o valor do observable “preco” nada acontecerá, por que não implementamos o “update”.

Update no binding handler
O update no binding handler funciona da mesma forma que o “init”, e com os mesmo parâmetros, a diferença é que o KO aciona ele quando o valor o observable atrelado muda, a implementação dele ficaria assim:

ko.bindingHandlers.textMoney = {
    init: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings().textMoneyOptions;
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(opcoes.quantDec));
    },
    update: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings().textMoneyOptions;
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(opcoes.quantDec));
    }
};

O “update” está exatamente igual ao “init”, vai do caso, no nosso, deveria estar mesmo, não existe um comportamento que deve ocorrer somente ao atualizar o observable, se deixar assim funcionará, mas também podemos seguir dois caminhos.
Primeiro caminho: No “update” chamamos a implementação de “init”, ficando dessa forma:

ko.bindingHandlers.textMoney = {
    init: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings().textMoneyOptions;
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(opcoes.quantDec));
    },
    update: function (element, valueAccessor, allBindings) {
        ko.bindingHandlers.textMoney.init(element, valueAccessor, allBindings);
    }
};

Segundo caminho: Implementar apenas o “update”, o “init” e o “update” não depende um do outro, um pode ser implementado de forma intrínseca.
http://jsfiddle.net/andrebtoe/gszLv62k

ko.bindingHandlers.textMoney = {
    update: function (element, valueAccessor, allBindings) {
        var opcoes = allBindings().textMoneyOptions;
        var valor = ko.unwrap(valueAccessor());
        var valorFlutuante = parseFloat(valor);
        if (isNaN(valorFlutuante))
            valorFlutuante = 0;
        
        $(element).text(opcoes.moeda + " " + valorFlutuante.toFixed(opcoes.quantDec));
    }
};

Mudamos também o html para que fique claro que o que ocorre ao mudar o valor o observable preço.

<input type="text" data-bind="value: preco" />
<div data-bind="textMoney: preco, textMoneyOptions: {moeda: 'R$', quantDec: 3}"></div>

Espero ter ajudado!

Publicidade