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="/bg#" 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, но потребителският интерфейс не приема промяната, освен ако не се свържа отново с целия viewModel с ko.mapping.fromJS(jsondata, viewModel);. Има ли по-добър начин да го накарам да обвърже отново само елемента, който актуализирам?   -  person solefald    schedule 12.11.2013


Отговори (1)


Както беше казано в коментарите, счита се за „лоша практика на Knockout“ да актуализирате стойност чрез 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