Обновление обработчиков связывания нокаута не запускается для изменений наблюдаемого массива

Я пытаюсь получить доступ к нескольким значениям через один обработчик связывания, в случае изменений observableArray, которые находятся внутри объекта привязки valuesAccessor, обновление обработчика связывания не запускается.

ko.bindingHandlers.chosen = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        console.log("INIT");
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
         var value = ko.unwrap(valueAccessor());

         ko.utils.arrayForEach(value,function(binding){            
                var value = ko.unwrap(binding);
        });
        console.log("IT WORKS!");
     }
  };

<select data-bind="
    options: Options,
     chosen: {options: Options}
"></select>

Демо: ( также на jsFiddle):

    ko.bindingHandlers.chosen = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            console.log("INIT");
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
             var value = ko.unwrap(valueAccessor());
             
             ko.utils.arrayForEach(value,function(binding){            
              var value = ko.unwrap(binding);
            });
            console.log("IT WORKS!");
        }
    };

    function Model() {

        this.Options = ko.observableArray(opt1);
        this.Reload = function () {
            if (!this.index) {
                this.Options(opt2);
                this.index = 1;
            } else {
                this.Options(opt1);
                this.index = 0;
            }
            this.Options.valueHasMutated();
        };
        this.index = 0;

    }

    var opt1 = [{
        Text: "1",
        Value: "1"
    }, {
        Text: "2",
        Value: "2"
    }, ];
    var opt2 = [{
        Text: "3",
        Value: "3"
    }, {
        Text: "4",
        Value: "4"
    }, ];
    ko.applyBindings(new Model());
<select data-bind="
        options: Options,
        value: Selection,
        optionsText: 'Text',
        optionsValue: 'Value',
        chosen: {options: Options}
    "></select>
<input type="button" data-bind="click: Reload" value="reload" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

1 ответ

Проблема в том, что ваш chosen привязка на самом деле не обращается к наблюдаемому массиву, поэтому при его изменении нокаут не вызывает ваш update обработчик, потому что он не думает, что что-то изменилось, что он использует.

С этой привязкой:

chosen: {options: Options}

Вы связываете совершенно новый объект (с options свойство, указывающее на ваш Options наблюдаемый массив). Это означает, что когда вы делаете

var value = ko.unwrap(valueAccessor());

value теперь содержит: {options: <observableArrayFunction>} - вам нужно получить доступ к options Свойство на него для нокаута зарегистрировать по ссылке:

ko.utils.arrayForEach(value.options(),function(binding){            

});

Если вы сделаете это изменение, вы увидите, что "IT WORKS!" теперь регистрируется при обновлении. На самом деле он регистрируется дважды, один раз, потому что вы обновляете массив, и другой, потому что вы звоните valueHasMutated,

Обновленный фрагмент (с дополнительным valueHasMutated удалены):

ko.bindingHandlers.chosen = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            console.log("INIT");
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
             var value = ko.unwrap(valueAccessor());
             
             ko.utils.arrayForEach(value.options(),function(binding){            
              var value = ko.unwrap(binding);
            });
            console.log("IT WORKS!");
        }
    };

    function Model() {

        this.Options = ko.observableArray(opt1);
        this.Reload = function () {
            if (!this.index) {
                this.Options(opt2);
                this.index = 1;
            } else {
                this.Options(opt1);
                this.index = 0;
            }
        };
        this.index = 0;

    }

    var opt1 = [{
        Text: "1",
        Value: "1"
    }, {
        Text: "2",
        Value: "2"
    }, ];
    var opt2 = [{
        Text: "3",
        Value: "3"
    }, {
        Text: "4",
        Value: "4"
    }, ];
    ko.applyBindings(new Model());
<select data-bind="
        options: Options,
        value: Selection,
        optionsText: 'Text',
        optionsValue: 'Value',
        chosen: {options: Options}
    "></select>
<input type="button" data-bind="click: Reload" value="reload" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

Другие вопросы по тегам