изменить событие на выбор с нокаутом привязки, как я могу знать, если его реальное изменение


Я создаю пользовательский интерфейс разрешений, у меня есть список разрешений со списком выбора рядом с каждым разрешением. Разрешения представлены наблюдаемым массивом объектов, которые привязаны к списку выбора:

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

теперь проблема заключается в следующем: событие изменения возникает, когда пользовательский интерфейс просто заполняется в первый раз. Я вызываю свою функцию ajax, получаю список разрешений, а затем событие вызывается для каждого из элементов разрешений. Это не поведение я хотеть. Я хочу, чтобы он был поднят только тогда, когда пользователь выбирает новое значение для разрешения в списке выберите, как я могу это сделать?

8 73

8 ответов:

на самом деле вы хотите узнать, вызвано ли событие пользователем или программой, и его очевидно, что событие будет срабатывать при инициализации.

нокаут подход добавления subscription не поможет во всех случаях, почему, потому что в большинстве моделей будет реализовано так

  1. init модель с неопределенными данными, просто структура (actual KO initilization)
  2. обновить модель с исходными данными (logical init like load JSON , get data etc)
  3. взаимодействие с пользователем и обновления

реальный шаг, который мы хотим захватить изменения в 3, но во втором шаге subscription получит звонок , Так что лучший способ, чтобы добавить к изменению события, как

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

и обнаружил событие в permissionChanged функции

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}

это только предположение, но я думаю, что это происходит потому, что level - число. В таком случае,value привязка вызовет a change событие для обновления level со строковым значением. Поэтому вы можете исправить это, убедившись level - это строка для начала.

кроме того, более "нокаутирующий" способ сделать это-не использовать обработчики событий, а использовать наблюдаемые и подписки. Сделай level наблюдаемый, а затем добавить подписку на него, который будет запущен всякий раз, когда level изменения.

вот это решение, которое может помочь с этим странным поведением. Я не мог найти лучшего решения, чем поместить кнопку, чтобы вручную вызвать событие изменения.

EDIT: может быть, пользовательская привязка, как это может помочь:

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
            }

        });

    }
  };

и в свой атрибут select data-bind добавьте:

changeSelectValue: yourSelectValue

Я использую пользовательскую привязку (на основе этой скрипка РП Нимейер, см. Его ответ на этой вопрос), который гарантирует, что числовое значение правильно преобразуется из строки в число (как это предлагается решением Майкла Беста):

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:

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

Если вы используете наблюдаемое вместо примитивного значения, select не будет вызывать события изменения при начальной привязке. Вы можете продолжить привязку к событию изменения, а не подписываться непосредственно на наблюдаемое.

быстрый и грязный, используя простой флаг:

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

Если это не работает, попробуйте обернуть последнюю строку в setTimeout() - события асинхронны, поэтому, возможно, последний все еще ожидает, когда applyBindings() уже вернулся.

У меня была аналогичная проблема, и я просто изменил обработчик событий, чтобы проверить тип переменной. Тип устанавливается только после выбора пользователем значения, а не при первой загрузке страницы.

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

Это, кажется, работает для меня.

Если вы работаете с нокаутом, используйте ключевую функциональность observable functionality knockout.
Используйте ko.computed() метод и сделать и вызвать ajax вызов в этой функции.