1

J'essaie de créer une liaison personnalisée dans knockout qui fonctionne comme le gestionnaire de liaison par défaut options, mais utilise des boutons radio au lieu d'une liste déroulante.Knockoutjs Radio Button Group Liaison personnalisée ne pas mettre à jour sur la sélection

L'ajout d'un élément à la baie notifiera le update mais la sélection d'un bouton radio différent ne le fera pas.

Note: J'ai dépouillé le gestionnaire de reliure personnalisé au strict minimum.

Reliure Handler

// LOOK FOR MY COMMENT //<------------------------------ LOOK FOR IT 
ko.bindingHandlers.radioButtons = { 
    update: function (element, valueAccessor, allBindings) { 
     var unwrappedArray = ko.utils.unwrapObservable(valueAccessor()); 
     var previousSelectedValue = ko.utils.unwrapObservable(allBindings().value); 


     // Helper function 
     var applyToObject = function (object, predicate, defaultValue) { 
      var predicateType = typeof predicate; 
      if (predicateType == "function") // Given a function; run it against the data value 
       return predicate(object); 
      else if (predicateType == "string") // Given a string; treat it as a property name on the data value 
       return object[predicate]; 
      else        // Given no optionsText arg; use the data value itself 
       return defaultValue; 
     }; 

     // Map the array to a newly created label and input. 
     var isFirstPass = true; 
     var radioButtonForArrayItem = function (arrayEntry, index, oldOptions) { 
      if (oldOptions.length) { 
       var item = oldOptions[0]; 
       if ($(item).is(":checked")) 
        previousSelectedValue = item.value; 

       isFirstPass = false; 
      } 

      var input = element.ownerDocument.createElement("input"); 
      input.type = "radio"; 

      var radioButtonGroupName = allBindings.get("groupName"); 
      input.name = radioButtonGroupName; 

      if (isFirstPass) { 
       var selectedValue = ko.utils.unwrapObservable(allBindings.get("value")); 
       var itemValue = ko.utils.unwrapObservable(arrayEntry.value); 
       if (selectedValue === itemValue) { 
        input.checked = true; 
       } 
      } else if ($(oldOptions[0].firstElementChild).is(":checked")) { 
       input.checked = true; 
      } 

      var radioButtonValue = applyToObject(arrayEntry, allBindings.get("radioButtonValue"), arrayEntry); 
      ko.selectExtensions.writeValue(input, ko.utils.unwrapObservable(radioButtonValue)); 

      var label = element.ownerDocument.createElement("label"); 
      label.appendChild(input); 
      var radioButtonText = applyToObject(arrayEntry, allBindings.get("radioButtonText"), arrayEntry); 
      label.append(" " + radioButtonText); 

      return [label]; 
     }; 

     var setSelectionCallback = function (arrayEntry, newOptions) { 
      var inputElement = newOptions[0].firstElementChild; 
      if ($(inputElement).is(":checked")) { 
       var newValue = inputElement.value; 
       if (previousSelectedValue !== newValue) { 
        var value = allBindings.get("value"); 
        value(newValue); //<------------------------- Get observable value and set here. Shouldn't this notify the update? 
       } 
      } 
     }; 

     ko.utils.setDomNodeChildrenFromArrayMapping(element, unwrappedArray, radioButtonForArrayItem, {}, setSelectionCallback); 
    }, 
}; 

Comment l'utiliser ...

// Html 
<div data-bind=" 
    radioButtons: letters, 
    groupName: 'letters', 
    radioButtonText: 'text', 
    radioButtonValue: 'value', 
    value: value,  
"></div> 

// Javascript 
var vm = { 
    letters: ko.observableArray([ 
     { 
      text: "A", 
      value: 1, 
     }, 
     { 
      text: "B", 
      value: 2, 
     }, 
    ]), 
    value: ko.observable(2), 
}; 

ko.applyBindings(vm); 

Ainsi, l'ajout d'un nouvel élément à vm.letters({ text: "C", value: 2 }) informera le update. Cependant, cliquer sur un bouton radio différent n'informera pas le update.

Que dois-je faire pour qu'un clic sur le bouton radio avertisse le update?

DEMO HERE

+0

j'ai ajouté un hack mais maintenant je me sens sale ... '$ (entrée) .on ("clic", function() {var value = allBindings.get ("valeur"); value (this.value);}); ' – christo8989

+0

J'ai également remarqué que je n'obtiens pas de liaison bidirectionnelle avec' vm.value' . – christo8989

Répondre

1

Il ne ressemble pas à votre liaison a une façon de mettre à jour l'observable lorsque la sélection change. La liaison bidirectionnelle n'est pas automatique avec les liaisons personnalisées, sauf si vous ne faites que passer des paramètres à une autre liaison existante. Les gestionnaires d'événements comme votre "hack" sont précisément ce que la fonction "init" d'une liaison personnalisée est habituellement utilisée.

La « init » rappel

Knockout appellera votre fonction init une fois pour chaque élément DOM vous utilisez> la liaison sur. Il y a deux utilisations principales init:

  • Pour définir tout état initial de l'élément DOM
  • Pour enregistrer les gestionnaires d'événements de sorte que, par exemple, lorsque l'utilisateur clique sur ou modifie l'élément DOM, vous peut changer l'état de l'observable associé

Essayez d'ajouter quelque chose comme la fonction init suivante à votre RadioButtons reliure:

ko.bindingHandlers.radioButtons = { 
    init: function(element, valueAccessor, allBindings){ 
    var unwrappedArray = ko.utils.unwrapObservable(valueAccessor()); 
    var value = allBindings().value; 

    ko.utils.registerEventHandler(element, "click", function(event){ 
     var target = event.target; 
     if(target.nodeName.toLowerCase() == "input"){ 
      value($(target).attr("value")); 
     } 
    }); 
    }, 
    update: function (element, valueAccessor, allBindings) { 
     ... 
    } 

Il existe probablement des moyens plus sûrs de gérer l'événement click que de vérifier si la cible est un élément "input"; c'est juste un exemple rapide. Vous devrez le modifier si vous avez déjà imbriqué des boutons radio ou d'autres types d'éléments d'entrée en tant qu'enfants dans votre élément radiobutton. En cas de doute, vous pouvez toujours copier à partir de la liaison désactivée "checked" source code.

EDIT: Certaines choses à réparer et comment je les ai réparées.

  1. Mettez à jour la propriété value de viewmodel.
  2. Mettez à jour la vue lorsque la propriété value est modifiée par programme (liaison bidirectionnelle).
  3. Si le bouton radio sélectionné est supprimé, je dois définir la propriété value sur undefined.

Il y a probablement une meilleure façon de réaliser tout cela ...

ko.bindingHandlers.radioButtons = { 
    init: function(element, valueAccessor, allBindings){ 
    //... error checks ... Remove all child elements to "element ... 

    var value = allBindings.get("value"); 

    // 1. Update viewmodel 
    ko.utils.registerEventHandler(element, "click", function(event){ 
     var target = event.target; 
     if(target.nodeName.toLowerCase() == "input"){ 
      value(target.value); 
     } 
    }); 

    // 2. Update view 
    value.subscribe(function (newValue) { 
     var inputs = element.getElementsByTagName("input") 
     $.each(inputs, function (i, input) { 
      input.checked = input.value === newValue; 
     }); 
    }; 
    }, 
    update: function (element, valueAccessor, allBindings) { 
     ... 

     var value = allBindings.get("value"); 

     // 3. Edge case: remove radio button of the value selected. 
     var selectedRadioButton = unwrappedArray.find(function (item) { 
      return item.value === value(); 
     }); 
     if (selectedRadioButton == null) { 
      value(undefined); 
     } 
    } 
}