web-dev-qa-db-fra.com

changer d'événement sur select avec knockout binding, comment puis-je savoir si c'est un vrai changement

Je construis une interface utilisateur d'autorisations, j'ai une liste d'autorisations avec une liste de sélection à côté de chaque autorisation. Les autorisations sont représentées par un tableau observable d'objets liés à une liste de sélection:

<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>

Maintenant, le problème est le suivant: l'événement change est déclenché lorsque l'interface utilisateur remplit pour la première fois. J'appelle ma fonction ajax, obtient la liste des autorisations, puis l'événement est déclenché pour chacun des éléments d'autorisation. Ce n'est vraiment pas le comportement que je veux. Je veux qu'il soit soulevé uniquement lorsqu'un utilisateur choisit réellement une nouvelle valeur pour l'autorisation dans la liste de sélection , comment puis-je le faire?

77
guy schaller

En fait vous voulez savoir si l'événement est déclenché par l'utilisateur ou le programme , et il est évident que cet événement se déclenchera pendant l'initialisation.

L’approche knock-out consistant à ajouter subscription n’aidera pas dans tous les cas, car, dans la plupart des cas, le modèle sera implémenté comme ceci

  1. initiez le modèle avec des données non définies, structurez simplement (actual KO initilization)
  2. mettre à jour le modèle avec les données initiales (logical init like load JSON , get data etc)
  3. Interaction utilisateur et mises à jour

L’étape réelle que nous voulons capturer est celle des modifications dans 3, mais à la deuxième étape, subscription sera appelé, Un meilleur moyen consiste donc à ajouter au changement d’événement de 

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

et a détecté l'événement dans la fonction permissionChanged 

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}
86
Sarath

Ceci est juste une hypothèse, mais je pense que cela se produit parce que level est un nombre. Dans ce cas, la liaison value déclenchera un événement change pour mettre à jour level avec la valeur de chaîne. Vous pouvez donc résoudre ce problème en vous assurant que level est une chaîne de départ.

De plus, la méthode la plus simple consiste à ne pas utiliser de gestionnaires d'événements, mais à utiliser des observables et des abonnements. Faites de level un observable, puis ajoutez-y un abonnement, qui sera exécuté à chaque changement de level.

32
Michael Best

Voici une solution qui peut aider avec ce comportement étrange. Je ne pouvais pas trouver de meilleure solution que de placer un bouton pour déclencher manuellement l'événement de modification.

EDIT: Peut-être qu'une reliure personnalisée comme celle-ci pourrait aider:

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
            }

        });

    }
  };

Et dans votre attribut select data-bind, ajoutez:

changeSelectValue: yourSelectValue
4
Ingro

J'utilise cette liaison personnalisée (basée sur this fiddle de RP Niemeyer, voir sa réponse à this question), qui permet de s'assurer que la valeur numérique est correctement convertie d'une chaîne à l'autre 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 });
    }
};

Exemple HTML:

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

Si vous utilisez une valeur observable au lieu d'une valeur primitive, la sélection ne déclenchera pas d'événements de modification lors de la liaison initiale. Vous pouvez continuer à vous lier à l'événement change plutôt que de vous abonner directement à l'observable.

2
Stefan Smith

Rapide et sale, utilisant un simple drapeau:

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

Si cela ne fonctionne pas, essayez d'encapsuler la dernière ligne dans setTimeout () - les événements sont asynchrones. Par conséquent, la dernière est peut-être toujours en attente lorsque applyBindings () est déjà renvoyé.

1
Niko

Si vous travaillez avec Knockout, utilisez les fonctionnalités clés de Knockout.
Utilisez la méthode ko.computed() et faites et déclenchez un appel ajax dans cette fonction.

0
vasu

J'ai eu un problème similaire et je viens de modifier le gestionnaire d'événements pour vérifier le type de la variable. Le type est défini uniquement après que l'utilisateur a sélectionné une valeur, et non lors du premier chargement de la page.

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

Cela semble fonctionner pour moi.

0
fireydude

utilisez ceci:

this.permissionChanged = function (obj, event) {

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

    } 
}
0
AGuyCalledGerald