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

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

<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, получаю список разрешений, а затем событие увеличивается для каждого элемента разрешения. Это действительно не то поведение, которое я хочу. Я хочу, чтобы он был поднят только тогда, когда пользователь действительно выбирает новое значение для разрешения в списке выбора, как я могу это сделать?

Ответ 1

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

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

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

Фактический шаг, который мы хотим захватить, - это изменения в 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

  }

}

Ответ 2

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

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

Ответ 3

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

РЕДАКТИРОВАТЬ: Может быть, пользовательское связывание, подобное этому, может помочь:

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
            }

        });

    }
  };

И в выбранном атрибуте привязки данных добавьте:

changeSelectValue: yourSelectValue

Ответ 4

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

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>

Ответ 5

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

Ответ 6

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

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() уже возвращен.

Ответ 7

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

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

Кажется, это работает для меня.

Ответ 8

использовать этот:

this.permissionChanged = function (obj, event) {

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

    } 
}

Ответ 9

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