изменить событие на выбор с нокаутом привязки, как я могу знать, если его реальное изменение
Я создаю пользовательский интерфейс разрешений, у меня есть список разрешений со списком выбора рядом с каждым разрешением. Разрешения представлены наблюдаемым массивом объектов, которые привязаны к списку выбора:
<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 ответов:
на самом деле вы хотите узнать, вызвано ли событие пользователем или программой, и его очевидно, что событие будет срабатывать при инициализации.
нокаут подход добавления
subscription
не поможет во всех случаях, почему, потому что в большинстве моделей будет реализовано так
- init модель с неопределенными данными, просто структура
(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 } }
это только предположение, но я думаю, что это происходит потому, что
level
- число. В таком случае,value
привязка вызовет achange
событие для обновления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') { ... } }
Это, кажется, работает для меня.