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

87

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

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

Гай Шаллер
источник

Ответы:

107

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

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

  1. инициализировать модель с неопределенными данными, просто структурировать (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

  }

}
Сарат
источник
5
Думаю, это следует отметить как правильный ответ. У меня был точный сценарий, описанный в вопросе, и я протестировал передачу строк в качестве ключа к selectэлементу. Однако даже когда начальное значение select совпадало с одним из вариантов, changeсобытие все равно запускалось . Когда я реализовал этот простой ifблок в обработчике, все заработало, как ожидалось. Сначала попробуйте этот метод. Спасибо, @Sarath!
Pflugs
Мне нравится концепция различения между измененным пользователем и измененной программой. Это полезно в повседневных случаях, особенно с нокаутом, когда наблюдаемые наиболее вероятно изменят значение без какого-либо взаимодействия с пользователем.
rodiwa 07
Согласитесь с @Pflugs, различие между типами событий сработало для меня, и это должен быть принятый ответ.
Эмма Миддлбрук
1
def должен быть принятым ответом ... свойство типа события может использоваться для дифференциации срабатывания premissionChanged
caniaskyouaquestion
1
Также использовался этот метод, чтобы определить, изменял ли пользователь выбор, а не другой триггер. В моем случае обновление значений параметров с помощью привязки «options» вызвало событие «change».
nath
32

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

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

Майкл Бест
источник
после 2 часов поиска причины, по которой моя форма запускает событие «изменение» после загрузки страницы, вы, как вы догадывались, спасли меня.
Samih A
Ваш ответ дал мне идею ... Я целенаправленно изменил тип, исходящий из URL-адреса, поэтому моя функция подписки срабатывала при загрузке страницы (я хотел, чтобы она обрабатывала переменную URL-адреса, а не только тогда, когда мой раскрывающийся список запускал изменение). Есть ли менее хитрый способ сделать это?
Jiffy
4

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

РЕДАКТИРОВАТЬ: Возможно, такая настраиваемая привязка может помочь:

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
Ingro
источник
о ... ты имеешь в виду, как кнопка сохранения? .. не подходит для меня .. мне действительно нужно, чтобы это работало :)
Гай Шаллер
Отредактируйте ответ с помощью лучшего решения (надеюсь).
Ingro
Привет, Ингро, это было помечено (неправильно) как не ответ. Я изменил формулировку вашего первоначального предложения, чтобы флагманы случайно не подумали, что вы публикуете вопрос в качестве ответа. Надеюсь это поможет! :)
jmort253 07
@ jmort253: извини, это моя вина. Лучше не отвечать, используя «У меня тоже такая же проблема», поскольку это выглядит так, будто вы спрашиваете что-то другое. Вместо этого предложите решение и объясните, как это работает.
Qantas 94 Heavy
1
Да извините, моя ошибка. Это был один из первых ответов, которые я опубликовал в stackoverflow и не знал всех правил. Спасибо за редактирование!
Ingro
3

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

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>
мху
источник
2

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

Стефан Смит
источник
1

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

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 () уже вернул.

Нико
источник
привет, спасибо за ответ, это не сработает для меня, потому что я вызываю ko.applyBindings, когда dom готов, но мои разрешения a заполняются моей моделью на более позднем этапе ... так что флаг будет истинным, но проблема все равно возникнет.
Гай Шаллер
0

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

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

Мне кажется, это работает.

Fireydude
источник
0

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

this.permissionChanged = function (obj, event) {

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

    } 
}
AGuyCalledGerald
источник
0

Попробуй это:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 
Чанака Сампатх
источник
2-й параметр не был индексом, для меня это было событием.
sairfan
@sairfan добавляет несколько параметров к функции и определяет, из какого параметра вы получаете событие, используя отладчик
Чанака Сампат
-1

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

васу
источник