KnockoutJS не улавливает событие jQuery .change()

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

Пример работы с этой скриптой JS: http://jsfiddle.net/rniemeyer/dtpfv/ Я пытаюсь реализовать грязный флаг. Единственное отличие состоит в том, что я изменяю данные внутри обычного диапазона по сравнению с полем ввода и использую плагин сопоставления, а не назначаю наблюдаемые вручную.

$(document).ready(function(){

    ko.dirtyFlag = function(root, isInitiallyDirty) {
        var result = function() {},
            _initialState = ko.observable(ko.toJSON(root)),
            _isInitiallyDirty = ko.observable(isInitiallyDirty);

        result.isDirty = ko.computed(function() {
            return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
        });
        result.reset = function() {
            _initialState(ko.toJSON(root));
            _isInitiallyDirty(false);
        };
        return result;

    };

    $.getJSON('/environments/data.json', function(jsondata) {
        var mapping = {
            create: function (options) {
                var innerModel = ko.mapping.fromJS(options.data);
                for (var i=0; i < innerModel.deployments().length; i++) {
                    innerModel.deployments()[i].dirtyFlag = new ko.dirtyFlag(innerModel.deployments()[i]);
                }
                return innerModel;
            }
        }

        var viewModel = ko.mapping.fromJS(jsondata, mapping);

        self.save = function() {
            console.log("Sending changes to server: " + ko.toJSON(this.dirtyItems));  
        };

        self.dirtyItems = ko.computed(function() {
            for (var i = 0; i < viewModel().length;  i++ ) {
                return ko.utils.arrayFilter(viewModel()[i].deployments(), function(deployment) {
                    return deployment.dirtyFlag.isDirty();
                });
            }
        }, viewModel);

        self.isDirty = ko.computed(function() {
            return self.dirtyItems().length > 0;
        }, viewModel);

        self.changeTag = function (data, event) {

            // Neither .change() nor .trigger('change') work for me
            $(event.target).parents().eq(4).find('span.uneditable-input').text(data.tag).change()

            // This value never changes.
            console.log('Dirty on Change: '+self.dirtyItems().length)

        }

        ko.applyBindings(viewModel);

    });

})

Вот урезанный фрагмент HTML, который запускает функцию changeTag(), которая заменяет current_tag() выбором из раскрывающегося меню. Однако это не запускает обновление KnockoutJS.

<div>
    <span data-bind="text: current_tag() }"></span>
    <div>
        <button data-toggle="dropdown">Select Tag</button>
        <ul>
          <!-- ko foreach: $.parseJSON(component.available_tags() || "null") -->
          <li><a href="#" data-bind="click: function(data, event) { changeTag(data, event) }"></a></li>
          <!-- /ko -->
        </ul>
    </div>
</div>

Я на второй день пытаюсь понять это. Любая идея, что я делаю неправильно здесь? Должен ли я использовать поле ввода, а не обычный элемент span? Нужно ли мне изменять значения моего viewModel напрямую вместо использования jQuery для управления DOM? (На самом деле я пробовал это, но изменение viewModel, а затем повторная привязка к нему, похоже, замедляют работу, если только я не делаю это неправильно)

Спасибо.


person solefald    schedule 12.11.2013    source источник
comment
в чем именно проблема?: Не могли бы вы определить это немного лучше. Я предполагаю, что вы говорите, что предоставленная вами скрипка является рабочим примером того, чего вы пытаетесь достичь - не могли бы вы предоставить скрипку со своим кодом?   -  person PW Kad    schedule 12.11.2013
comment
@PWKad: проблема в том, что KnckoutJS не получает обновления диапазона, которые я делаю с помощью jQuery. Создание скрипки прямо сейчас   -  person solefald    schedule 12.11.2013
comment
Я не уверен на 100%, почему вы хотите обновить наблюдаемое из jQuery - это противоречит основным принципам использования Knockout для двусторонней привязки данных. Может быть хороший вариант использования, но почему бы просто не обновить его, используя свойство, вместо того, чтобы переопределять его с помощью jQuery и потенциально нарушать вашу привязку?   -  person PW Kad    schedule 12.11.2013
comment
@PWKad: вот почему я здесь :) Я не разработчик, к тому же это хобби-проект, так что я понятия не имею, чем занимаюсь.   -  person solefald    schedule 12.11.2013
comment
@PWKad: на самом деле я пытался обновить свойство напрямую (упомянул об этом в нижней части моего поста), используя что-то вроде этого jsondata[environment()].deployments[deployment()].current_tag = ko.dataFor(event.target).tag, однако пользовательский интерфейс не улавливает изменение, если только я повторно не привязываюсь ко всей модели представления с помощью ko.mapping.fromJS(jsondata, viewModel);. Есть ли лучший способ повторно связать только тот элемент, который я обновляю?   -  person solefald    schedule 12.11.2013


Ответы (1)


Как сказано в комментариях, обновление значения через jQuery считается «плохой практикой нокаута», когда вы также можете напрямую обновить наблюдаемое. Knockout продвигает подход, основанный на данных.

В ответ на ваш последний комментарий (еще не знаю, как отвечать на комментарии о переполнении стека): причина, по которой пользовательский интерфейс не улавливает изменение, заключается в том, что вы неправильно присвоили значение:

var x = ko.observable(1); // x is now observable
x = 3; // x is no longer observable. After all, you've assigned the value 3 to it. It is now just a number
x(3); // this is what you're after. x is still an observable, and you assigned a new value to it by using Knockout's parentheses syntax. If x is bound to the ui somewhere, you'll see the value 3 appear

Итак, вы хотите сделать

jsondata[environment()].deployments[deployment()].current_tag(ko.dataFor(event.target).tag);
person Hans Roerdinkholder    schedule 12.11.2013
comment
Спасибо, Ханс, но у меня это не работает. Я получаю Uncaught TypeError: Property 'current_tag' of object #<Object> is not a function. Мое сопоставление как-то сломано и не создает наблюдаемое из current_tag? - person solefald; 13.11.2013
comment
Не бери в голову! Догадаться. Вместо этого мне пришлось использовать viewModel()[environment()].deployments()[deployment()].current_tag(ko.dataFor(event.target).tag);. Отметить как решенное! Еще раз спасибо! - person solefald; 13.11.2013