KnockoutJS: добавление наблюдаемых свойств и функций к объектам в сопоставлении, сгенерированном ObservableArray

Я новичок в KnockoutJS, и я застрял, пытаясь добавить дополнительные свойства и методы к сгенерированным объектам в ko.observableArray() созданный плагином mapping.


Вот чем я занимаюсь:

  • У меня есть массив JSON из Users
  • Я создал ko.observableArray() с помощью плагина сопоставления
  • У меня есть шаблон, который создает строку таблицы для каждого User, пока все хорошо :о)


Вот что я пытаюсь сделать:

У каждого User есть свойство с именем 'IsActive'. Я хотел бы data-bind щелкнуть событие по методу для каждого объекта User, который переключает это свойство 'IsActive'.

Этот вопрос выглядел многообещающе, но мне кажется ненужным дублирование, чтобы объявить всю модель представления в JS (если только я не должен это делать!) - можно ли просто расширить сгенерированный объект?

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


Вот код: http://jsfiddle.net/yZkSf/2/ (еще не работает в скрипте JS, но я продолжу играть с ним и обновлю эту ссылку, когда он заработает).

Спасибо за помощь


person Andrew    schedule 06.12.2011    source источник


Ответы (3)


Есть несколько вариантов, которые вы могли бы рассмотреть.

- Один из них – использовать create обратный вызов, чтобы контролировать, как ваши "пользовательские" объекты получают созданный. Вы можете либо самостоятельно определить наблюдаемые объекты и добавить дополнительные функции, либо вызвать подключаемый модуль сопоставления для отдельного пользовательского объекта, а затем добавить дополнительные функции.

Что-то вроде: http://jsfiddle.net/rniemeyer/fkVaK/

- В противном случае вы можете поместить функцию «переключатель» в свою модель представления, а затем передать ей объект «пользователь».

Хороший способ с 1.3 - использовать ko.dataFor вместе с чем-то вроде функций jQuery live/delegate/on event. Будет выглядеть так: http://jsfiddle.net/rniemeyer/FkjNr/

//unobtrusive event handler
$(".toggle").live("click", function() {
    var user = ko.dataFor(this);
    if (user) {
       viewModel.toggleIsActive(user);
    }
});

Если вы не хотите использовать делегирование событий, вы можете передать элемент напрямую, используя анонимную функцию, например: http://jsfiddle.net/rniemeyer/GpQtN/

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

<a href="#" data-bind="click: $root.toggleIsActive"><span data-bind="text: IsActive"></span></a>
person RP Niemeyer    schedule 06.12.2011
comment
Идеально! - Я был близок с обратным вызовом create:, но документация посвящена изменению дочернего массива, и я не мог понять, как увеличить базовый объект. Большое спасибо - person Andrew; 06.12.2011

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

JS:

$(function() {
    $.get("users/getUsers", function(r){
        var vm = ko.mapping.fromJS(r, {
            users: {
                create: function(user){
                    var methods = {
                        toggleIsActive: function(u){u.IsActive(!u.IsActive());},
                        foo: function(u){console.log(u);},
                        bar: function(u){/*whatever*/},   
                    }
                    return $.extend(ko.mapping.fromJS(user.data), methods);
                }
            }
        });
        ko.applyBindings(vm);
    }, 'json');
});

ДОМ:

<!-- ko foreach: users -->
   <a href="#" data-bind="click: toggleIsActive"><span data-bind="text: IsActive"></span></a>
<!-- /ko -->
person Jordan Arseno    schedule 27.06.2012

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

EDIT: Как предлагает RP Niemeyer в своем ответе! ;o)

В любом случае, один из способов добавить свойства к существующему объекту — использовать jQuery extend() для комбинировать объекты.

Во-первых, объявите дополнительные свойства и функции в новом объекте:

var userModel = {
    toggleIsActive: function() {
        console.log('toggleIsActive called: before: ' + this.IsActive());
        this.IsActive(!this.IsActive());
        // todo: save!
        console.log('toggleIsActive called: after: ' + this.IsActive());
    }
}

Затем, после вызова ko.mapping.fromJS(), но до вызова ko.applyBindings(), выполните цикл по объектам в сгенерированном массиве и расширьте их:

viewModel.users = ko.mapping.fromJSON(/* get JSON */);

for (var i = 0; i < viewModel.users().length; i++) {
    $.extend(viewModel.users()[i], userModel);
}

ko.applyBindings(viewModel);
person Andrew    schedule 06.12.2011