Создание вложенных моделей в Backbone с помощью Backbone-relational

Я хотел бы использовать backbone-relational, чтобы иметь вложенные модели в моем backbone.js.

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

Итак, мой вопрос: как мне расширить руководство по Todos с помощью backbone-relational so тот:

  • можно добавлять/удалять подэлементы для каждого элемента
  • двойной щелчок по любому подэлементу редактирует его (как и в оригинальном примере Todo)
  • щелчок по элементу скрывает/отображает его подэлементы
  • подэлементы не извлекаются отдельно, а являются просто атрибутом массива элементов Todo

Обновление: я создал jsfiddle для этого вопроса. Пока у меня есть:

  • Импортирован пример Todo, упомянутый выше
  • Создал модель TodoSubitem и коллекцию TodoSubitemList
  • Изменена модель Todo, чтобы расширить RelationalModel вместо Model, с отношением HasMany к TodoSubitem.
  • Добавлен subitem-template в html код

Но я все еще не уверен, как:

  • добавить поле ввода для subitems, которое появляется только при нажатии на Todo div
  • иметь данные подэлемента в качестве атрибута Todo объектов, но при этом TodoSubitemView привязывать к ним элементы DOM (например, <li> теги).

person Christopher DuBois    schedule 29.08.2011    source источник


Ответы (4)


Я не думаю, что в этом случае я бы создал отдельный «TodoSubItem» — почему бы не создать отношение HasMany из Todo->Todo, чтобы задача могла иметь 0..* children и 0..1 parent?

Таким образом, вы можете повторно использовать логику порядка (если вы измените ее, чтобы применить к коллекции), можете создать более глубокие уровни вложенности по желанию (или ограничить это до определенной глубины, если хотите) и т. д. Ряд однако для этого нужно будет обновить некоторые вещи - например, сохранить список дочерних представлений, чтобы вы могли перебирать их, чтобы пометить каждое как выполненное, и поддерживать (и обновлять) порядок на TodoList.

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

//Our basic **Todo** model has `text`, `order`, and `done` attributes.
window.Todo = Backbone.RelationalModel.extend({

    relations: [{
        type: Backbone.HasMany,
        key: 'children',
        relatedModel: 'Todo',
        collectionType: 'TodoList',
        reverseRelation: {
            key: 'parent',
            includeInJSON: 'id'
        }
    }],

    initialize: function() {
        if ( !this.get('order') && this.get( 'parent' ) ) {
            this.set( { order: this.get( 'parent' ).nextChildIndex() } );
        }
    },

    // Default attributes for a todo item.
    defaults: function() {
        return { done: false };
    },

    // Toggle the `done` state of this todo item.
    toggle: function() {
        this.save({done: !this.get("done")});
    }

    nextChildIndex: function() {
        var children = this.get( 'children' );
        return children && children.length || 0;
    }
});


// The DOM element for a todo item...
window.TodoView = Backbone.View.extend({

    //... is a list tag.
    tagName:  "li",

    // Cache the template function for a single item.
    template: _.template($('#item-template').html()),

    // The DOM events specific to an item.
    events: {
        'click': 'toggleChildren',
        'keypress input.add-child': 'addChild',
        "click .check"              : "toggleDone",
        "dblclick div.todo-text"    : "edit",
        "click span.todo-destroy"   : "clear",
        "keypress .todo-input"      : "updateOnEnter"
    },

    // The TodoView listens for changes to its model, re-rendering.
    initialize: function() {
        this.model.bind('change', this.render, this);
        this.model.bind('destroy', this.remove, this);

        this.model.bind( 'update:children', this.renderChild );
        this.model.bind( 'add:children', this.renderChild );

        this.el = $( this.el );

        this.childViews = {};
    },

    // Re-render the contents of the todo item.
    render: function() {
        this.el.html(this.template(this.model.toJSON()));
        this.setText();

        // Might want to add this to the template of course
        this.el.append( '<ul>', { 'class': 'children' } ).append( '<input>', { type: 'text', 'class': 'add-child' } );

        _.each( this.get( 'children' ), function( child ) {
            this.renderChild( child );
        }, this );

        return this;
    },

    addChild: function( text) {
        if ( e.keyCode == 13 ) {
            var text = this.el.find( 'input.add-child' ).text();
            var child = new Todo( { parent: this.model, text: text } );
        }
    },

    renderChild: function( model ) {
        var childView = new TodoView( { model: model } );
        this.childViews[ model.cid ] = childView;
        this.el.find( 'ul.children' ).append( childView.render() );
    },

    toggleChildren: function() {
        $(this.el).find( 'ul.children' ).toggle();
    },

    // Toggle the `"done"` state of the model.
    toggleDone: function() {
        this.model.toggle();
        _.each( this.childViews, function( child ) {
            child.model.toggle();
        });
    },

    clear: function() {
        this.model.set( { parent: null } );
        this.model.destroy();
    }

    // And so on...
});
person Paul    schedule 31.08.2011
comment
Спасибо, Пол! Я постараюсь включить это в свой пример. - person Christopher DuBois; 01.09.2011
comment
Полный пример кода был бы хорош - я не могу понять это. - person Devin McQueeney; 09.10.2012
comment
Если я не ошибаюсь, это не работает, потому что для Backbone-relational требуется, чтобы связанная модель была определена, прежде чем она сможет создавать отношения между моделями. - person Jon Biz; 09.11.2012

Я не думаю, что вы можете создавать самостоятельные модели в Backbone-relational (как описано в другом ответе здесь). Когда я попробовал это, я получаю сообщение об ошибке: Backbone-relational требует, чтобы связанная модель была определена, прежде чем она сможет создавать с ней отношения.

Итак, я изменил шаблон «многие ко многим», описанный на странице, посвященной магистральным сетям:

https://github.com/PaulUithol/Backbone-relational#many-to-many-relations

По сути, я создаю модель связывания, содержащую ссылки на модель, на которую ссылаются, чтобы эта модель ссылок могла быть доступна для Backbone-relational, когда она определяет реальную модель.

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

Давайте создадим модель «Person», у которой есть дети, которые являются другими моделями «Person».

Person = Backbone.RelationalModel.extend({
relations: [
    {
        type: 'HasMany',
        key: 'Children',
        relatedModel: 'FamilyRelation',
        reverseRelation: {
            key: 'Childrenof'
        }
    },
    {
        type: 'HasMany',
        key: 'Parent',
        relatedModel: 'FamilyRelation',
        reverseRelation: {
            key: 'Parentof'
        }
    }
]
});

FamilyRelation должен быть определен >до‹ Person, чтобы Backbone-relational мог создавать связи, так что это идет перед определением модели Person в вашем коде:

// FamilyRelation is link model between two "Person"s 
// to achieve the Fan/Admiree relation.

FamilyRelation = Backbone.RelationalModel.extend({
})

Если мы создадим два "Person":

KingKong = new Person({name: 'KingKong'});
SonOfKong = new Person({name: 'SonOfKong'});

Затем мы можем создать модель FamilyRelationship, которая является родительской для SonOfKong, и добавить ее к дочерним элементам KingKong с помощью этой строки:

KingKong.get("children").add({"parentof":SonOfKong});

Затем вы можете добавить удобные функции в модель Person, чтобы получить вложенные модели из модели FamilyRelationship, и вам больше не нужно трогать FamilyRelation, кроме как убедиться, что она сохраняется и извлекается надлежащим образом.

Для неиерархических отношений (скажем, «Друг», а не «Родитель/дочерний»), вам по-прежнему нужны эти два отношения с моделью связывания, чтобы иметь возможность извлекать одно из другого, что является своего рода хаком, но оно работает.

person Jon Biz    schedule 09.11.2012

После некоторого возни я нашел способ создать настоящую вложенную модель:

var theModel = Backbone.RelationalModel.extend({ [...] });
theModel.prototype.relations.push({
  type: Backbone.HasOne,
  key: 'key',
  relatedModel: theModel
});

В момент использования модели (при отправке отношений в прототип) она доступна, поэтому все работает.

person Ronald Mansveld    schedule 19.06.2014
comment
Вау, так просто. Жаль, что я не нашел это несколько часов назад. - person twiz; 25.09.2014

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

Чтобы создать самореферентную модель, вы просто опускаете relatedModel. Что-то вроде этого:

Person = Backbone.RelationalModel.extend({ relations: [{ type: 'HasMany', key: 'Children', }] })

Это объясняется в документации.

person tvdw    schedule 17.06.2016