1

options 기본 바인딩 처리기와 같은 기능을하지만 드롭 다운 대신 라디오 단추를 사용하는 녹아웃에서 사용자 지정 바인딩을 만들려고합니다.Knockoutjs 라디오 단추 그룹 사용자 지정 바인딩 선택시 업데이트 중

배열에 항목을 추가하면 update에 알리지 만 다른 라디오 버튼을 선택하면 알리지 않습니다.

참고 : 사용자 지정 바인딩 처리기를 최소한으로 제거했습니다.

// 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); 
    }, 
}; 

사용하는 방법 핸들러

바인딩

...

// 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); 

그래서, vm.letters({ text: "C", value: 2 })에 새 항목을 추가하는 것은 update을 통지합니다. 그러나 다른 라디오 버튼을 클릭해도 update에 알리지 않습니다.

라디오 버튼을 클릭하면 update에 알릴 수 있도록하려면 어떻게해야합니까?

DEMO HERE

+0

나는 해킹을 추가하지만 ...'$ (입력) CSTE 연구진은 ("클릭"지금은 더러운 느낌, – christo8989

+0

나는 vm.value로 양방향 바인딩을 얻지 못한다는 것을 알아 챘다. (vm_value) . – christo8989

답변

1

그것은 당신의 결합처럼 보이지 않는 때 선택 항목이 변경 관찰을 업데이트 할 수있는 방법이있다. 매개 변수를 다른 기존 바인딩에 전달하지 않는 한 양방향 바인딩은 사용자 지정 바인딩에서 자동으로 수행되지 않습니다. "해킹"과 같은 이벤트 핸들러는 사용자 정의 바인딩의 "init"기능이 일반적으로 사용되는 것입니다.

은 "초기화"콜백

넉 아웃은 사용 하는 각 DOM 요소에 대해 한 번 초기화 함수를 호출합니다> 온 바인딩. 두 가지 주요 용도는 초기화에 대한 있습니다

  • 모든 이벤트 핸들러를 등록하려면 DOM 요소에 대해 어떤 초기 상태를 설정하려면, 그래서 예를 들어, 사용자의 클릭하거나 DOM 요소를 수정할 때, 당신 당신의 라디오 버튼에 다음 init 함수 같은 것을 추가 관련 관찰

시도의 상태를 변경할 수 있습니다 바인딩 :

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) { 
     ... 
    } 

대상이 "input"요소인지 확인하는 것보다 click 이벤트를 처리하는 것이 더 안전한 방법 일 수 있습니다. 그것은 단지 간단한 예입니다. 중첩 된 라디오 버튼 또는 다른 유형의 입력 요소가 라디오 버튼 요소 내에 자식으로있는 경우이를 수정해야합니다. 확실하지 않은 경우 항상 knockout "checked"바인딩 source code에서 복사 할 수 있습니다.

수정 : 수정해야 할 사항과 해결 방법.

  1. viewmodel의 value 속성을 업데이트하십시오.
  2. value 속성을 프로그래밍 방식으로 변경하면보기가 업데이트됩니다 (양방향 바인딩).
  3. 선택한 라디오 버튼이 제거 된 경우 value 속성을 undefined로 설정해야합니다. 이 모든 것을 달성하기 위해 더 나은 방법은 아마이

...

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); 
     } 
    } 
}