Шаблон для модификации нокаута, наблюдаемого на родительском объекте с модели детского просмотра

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

1] Передайте ссылку родительского свойства дочернему элементу и обновите свойство из дочернего элемента:

var ParentViewModel = function(){
    var self = this;
    this.selectedItem = ko.observable();
    this.child = ko.observable(new ChildViewModel(self.selectedItem));
}

var ChildViewModel = function(parentSelectedItem){
    var self = this;
    this.id = ko.observable();
    this.parentSelectedItem = parentSelectedItem;
    this.select = function(){
        self.parentSelectedItem(self);
    }
}

2] Создайте дочерний метод выбора родителя и укажите локальный наблюдаемый локально:

var ParentViewModel = function(){
    var self = this;
    this.selectedItem = ko.observable();

    var child = new ChildViewModel();
    child.select = function(){
        self.selectedItem(child);
    }
    this.child = ko.observable(child);
}

var ChildViewModel = function(){
    this.id = ko.observable();
}

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

Есть ли у кого-нибудь другие предложения шаблонов относительно того, как эта операция может быть достигнута в javascript в чистой и проверяемой форме? Или я более или менее застрял с этими двумя вариантами?

Ответы

Ответ 1

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

Затем в вашей привязке вы можете привязываться к $root.selectChild или $parent.selectChild. Первым аргументом, переданным обработчику, связанному с привязкой к клику/событию, являются фактические данные (в KO 2.0), поэтому ваш метод может жить на родительском объекте и получать его в качестве первого аргумента.

var Item = function(id, name) {
    this.id = id;
    this.name = ko.observable(name);    
};

var ViewModel = function() {
    var self = this;
    this.items = ko.observableArray([
        new Item(1, "one"),
        new Item(2, "two"),
        new Item(3, "three")
    ]);  

    this.selectedItem = ko.observable();

    this.selectItem = function(item) {
        self.selectedItem(item);
    };     
};

В этом случае ваша привязка будет выглядеть так:

<ul data-bind="foreach: items">
    <li>
        <a href="#" data-bind="text: name, click: $root.selectItem"></a>
    </li>
</ul>

Здесь он находится в jsFiddle: http://jsfiddle.net/rniemeyer/anRsA/

Вы даже можете упростить его. Наблюдаемые функции - это функции, и первый аргумент, который вы передаете им, используется для установки их значения, поэтому вы даже можете не включать метод selectItem и просто привязываться к $root.selectedItem напрямую (будет выглядеть так: http://jsfiddle.net/rniemeyer/anRsA/1/). Обычно я использую отдельный метод, чтобы быть явным, чтобы дать ему правильное имя (действие), и в случае, если есть дополнительная обработка, которая должна быть выполнена до или после установки элемента.

До KO 2.0 (где $root и $parent были введены вместе с изменением для передачи данных в качестве первого аргумента для обработчиков click и event), я использовал первый метод, который вы предложил совсем немного. Одна вещь, которую вы можете сделать там, на самом деле не создает дочернее свойство (this.parentSelectedItem) и просто ссылается на parentSelectedItem (который был передан как аргумент) непосредственно в методе select, поскольку он будет доступен в функции, потому что созданного замыкания.