alterar evento na seleção com ligação knockout, como posso saber se é uma mudança real


87

Estou construindo uma IU de permissões, tenho uma lista de permissões com uma lista de seleção ao lado de cada permissão. As permissões são representadas por uma matriz observável de objetos que estão vinculados a uma lista de seleção:

<div data-bind="foreach: permissions">
     <div class="permission_row">
          <span data-bind="text: name"></span>
          <select data-bind="value: level, event:{ change: $parent.permissionChanged}">
                   <option value="0"></option>
                   <option value="1">R</option>
                   <option value="2">RW</option>
           </select>
      </div>
 </div>

Agora, o problema é o seguinte: o evento de mudança é gerado quando a IU está sendo preenchida pela primeira vez. Eu chamo minha função ajax, obtenho a lista de permissões e, em seguida, o evento é gerado para cada um dos itens de permissão. Este não é realmente o comportamento que desejo. Quero que seja gerado apenas quando um usuário realmente escolher um novo valor para a permissão na lista de seleção , como posso fazer isso?

Respostas:


107

Na verdade, você deseja descobrir se o evento é disparado pelo usuário ou programa e é óbvio que o evento será disparado durante a inicialização.

A abordagem knockout de adicionar subscriptionnão ajudará em todos os casos, porque na maioria do modelo será implementado assim

  1. init o modelo com dados indefinidos, apenas estrutura (actual KO initilization)
  2. atualize o modelo com os dados iniciais (logical init like load JSON , get data etc)
  3. Interação e atualizações do usuário

A etapa real que queremos capturar são as alterações no 3, mas na segunda etapa subscriptionreceberemos uma chamada. Portanto, a melhor maneira é adicionar alterações ao evento como

 <select data-bind="value: level, event:{ change: $parent.permissionChanged}">

e detectou o evento em permissionChangedfunção

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

5
Acho que essa deve ser marcada como a resposta correta. Tive o cenário exato conforme descrito na pergunta e testei a passagem de strings como a chave para o selectelemento. No entanto, mesmo quando o valor inicial do select correspondeu a uma das opções, ele ainda acionou o changeevento. Quando implementei esse ifbloco simples no manipulador, tudo funcionou conforme o esperado. Tente este método primeiro. Obrigado, @Sarath!
Pflugs

Eu gosto do conceito de diferenciar entre usuário alterado e programa alterado. Isso é útil em casos cotidianos, especialmente com knockout, onde os observáveis ​​têm maior probabilidade de alterar o valor sem qualquer interação do usuário.
rodiwa

Concordo com @Pflugs, diferenciar entre os tipos de eventos funcionou para mim e esta deve ser a resposta aceita.
Emma Middlebrook

1
def deve ser uma resposta aceita ... a propriedade do tipo de evento pode ser usada para diferenciar o acionamento da premissionChanged
caniaskyouaquestion

1
Também usei este método para detectar se um usuário estava alterando uma seleção em vez de outro acionador. No meu caso, a atualização dos valores das opções por meio da ligação "options" acionou o evento "change".
nath

32

É apenas um palpite, mas acho que está acontecendo porque levelé um número. Nesse caso, a valueligação irá disparar um changeevento para atualizar levelcom o valor da string. Você pode corrigir isso, portanto, certificando- levelse de que é uma string para começar.

Além disso, a maneira mais "Knockout" de fazer isso é não usar manipuladores de eventos, mas usar observáveis ​​e assinaturas. Faça levelum observável e adicione uma assinatura a ele, que será executado sempre levelque houver alterações.


depois de 2 horas cavando a causa do meu formulário acionando o evento 'alterar' após o carregamento da página, você adivinhando me salvou.
Samih A

Sua resposta me deu uma ideia ... Eu alterei propositalmente o tipo vindo do url, para que minha função de inscrição fosse acionada no carregamento da página (eu queria que ela processasse uma var de url, não apenas quando meu menu suspenso acionasse uma alteração). Existe uma maneira menos hacky de fazer isso?
Jiffy,

4

Aqui está uma solução que pode ajudar com esse comportamento estranho. Não consegui encontrar uma solução melhor do que colocar um botão para acionar manualmente o evento de alteração.

EDITAR: Talvez uma ligação personalizada como esta possa ajudar:

ko.bindingHandlers.changeSelectValue = {

   init: function(element,valueAccessor){

        $(element).change(function(){

            var value = $(element).val();

            if($(element).is(":focus")){

                  //Do whatever you want with the new value
            }

        });

    }
  };

E em seu atributo de vinculação de dados selecionado, adicione:

changeSelectValue: yourSelectValue

oh ... você quer dizer um botão salvar? .. não é bom para mim .. eu realmente preciso que isso funcione :)
cara schaller

Edite a resposta com uma solução melhor (com sorte).
Ingro

Ei, Ingro, isso foi sinalizado (incorretamente) como não uma resposta. Eu reformulei sua frase inicial para que os sinalizadores não pensem acidentalmente que você está postando uma pergunta como resposta. Espero que isto ajude! :)
jmort253

@ jmort253: desculpe, foi minha culpa. É melhor não responder usando "Eu também tenho o mesmo problema", pois parece que você está perguntando outra coisa. Em vez disso, forneça diretamente uma solução para uma resposta e explique como ela funciona.
Qantas 94 Heavy de

1
Sim, desculpe, meu erro. Foi uma das primeiras respostas que postei no stackoverflow e não conhecia todas as regras. Obrigado pela edição!
Ingro

3

Eu uso esta ligação personalizada (com base neste violino de RP Niemeyer, veja sua resposta a esta pergunta), que garante que o valor numérico seja convertido corretamente de string para número (como sugerido pela solução de Michael Best):

Javascript:

ko.bindingHandlers.valueAsNumber = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function () {
                    var val = ko.utils.unwrapObservable(observable);
                    return (observable() ? observable().toString() : observable());
                },
                write: function (newValue) {
                    observable(newValue ? parseInt(newValue, 10) : newValue);
                },
                owner: this
            });
        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

HTML de exemplo:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>

2

Se você usar um valor observável em vez de um valor primitivo, o select não levantará eventos de mudança na ligação inicial. Você pode continuar a vincular-se ao evento de mudança, em vez de se inscrever diretamente no observável.


1

Rápido e sujo, utilizando um sinalizador simples:

var bindingsApplied = false;

var ViewModel = function() {
    // ...

    this.permissionChanged = function() {
        // ignore, if flag not set
        if (!flag) return;

        // ...
    };
};

ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true

Se isso não funcionar, tente quebrar a última linha em um setTimeout () - os eventos são assíncronos, então talvez o último ainda esteja pendente quando applyBindings () já retornou.


oi obrigado pela resposta, isso não vai funcionar para mim porque eu chamo o ko.applyBindings quando o dom estiver pronto, mas minhas permissões estão sendo preenchidas para meu modelo em um estágio posterior ... então a sinalização seria verdadeira, mas o problema ainda aconteceria.
cara schaller

0

Tive um problema semelhante e acabei de modificar o manipulador de eventos para verificar o tipo da variável. O tipo só é definido depois que o usuário seleciona um valor, não quando a página é carregada pela primeira vez.

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

Isso parece funcionar para mim.


0

usa isto:

this.permissionChanged = function (obj, event) {

    if (event.type != "load") {

    } 
}

0

Tente este:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 

O 2º parâmetro não era índice, para mim era evento.
sairfan

@sairfan adiciona alguns parâmetros à função e descobre de qual parâmetro você obtém o evento usando o depurador
Chanaka Sampath

-1

Se você estiver trabalhando com Knockout, use a funcionalidade principal de knockout de funcionalidade observável.
Use o ko.computed()método e faça e acione a chamada ajax dentro dessa função.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.