Реализация составного шаблона с использованием MVC/Backbone.js

My webapp имеет составную структуру, то есть каждая коллекция категорий может содержать смесь отдельных элементов и других категорий в качестве своих строк/узлов/дочерних элементов (не обязательно для правильной терминологии здесь). На самом деле это немного проще, поскольку каждая коллекция представлена ​​моделью Category, поэтому по существу каждая коллекция категорий имеет в качестве своих моделей модели Item и Category.

В целом, является ли это целесообразным способом реализации этой структуры с использованием MVC? Более конкретно, в Backbone.js возможно, чтобы коллекция имела модель factory (взяв json и вычислив, какую модель сгенерировать на основе структуры json) вместо свойства статической модели?

Ответ 1

Я предполагаю, что вы получаете список категорий/элементов в JSON, который выглядит примерно так...

{
    'id': 1,
    'name': 'My 1st Category',
    'children': [
        {
            'id': 2,
            'name': 'My 2nd Category',
            'children': []
        },
        {
            'id': 1,
            'name': 'An Item',
            'price': 109.99
        }
    ]
}

Backbone.js не имеет ничего из коробки, которая поддерживает несколько моделей в коллекции, но также не имеет ограничений на типы моделей, которые вы помещаете в коллекцию.

Указание типа модели в определении коллекции только делает одно, это позволяет Backbone знать, какой тип модели следует создавать, если вы передаете исходный JSON в коллекцию вместо объекта Backbone.Model. Если вы добавите модель Item в коллекцию, которая уже содержит несколько моделей Category, у Backbone не будет проблем с отображением ее в списке моделей; он не проверяет тип.

Таким образом, имея в виду, вы можете использовать почти все, что предлагает коллекция, за исключением передачи его raw JSON; вам нужно будет справиться с этим сами. Таким образом, ваш выбор состоит в том, чтобы заранее создать ваши модели, превратив их в объекты Backbone.Model или создать что-то, что сделает синтаксический анализ для вас.

Для второго варианта анализатор я бы предложил передать специальную переменную в коллекцию, содержащую ваш сырой JSON, а затем обработать это в вашей функции initialize. Вот пример:

var CategoryCollection = Backbone.Collection.extend({
    initialize: function(m, models) {
        _.each(models, function(model) {
            var modelObject = null;
            if (model.price !== undefined) {
                modelObject = new Item(model);
            } else {
                modelObject = new Category(model);
            }

            this.add(modelObject);
        }, this);
    }
});

Итак, это немного взломанно, но вы определяете тип модели, основываясь на том, имеет ли оно определенное поле (price в моем примере), создайте объект модели, а затем добавьте его в коллекцию.

Вы бы назвали это следующим образом:

var myCollection = new CategoryCollection([], myJSON);

Обратите внимание, что вам нужно передать пустой массив в качестве первого аргумента, так как вы обычно передавали набор моделей в коллекцию.

Позже, используя коллекцию, вы можете определить, имеете ли вы дело с Item или Category с помощью простой проверки instanceof:

_.each(myCollection.models, function(model) {
    if (model instanceof Item) {
        console.log("It an Item! Price: ", model.get("price"));
    } else {
        console.log("It a Category!");
    }
});

Ответ 3

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

Во-первых, когда вы извлекаете модель из базы данных, вы должны перезаписать метод модели parse для создания моделей, представляющих заданные элементы из вашего примера.

Затем при сохранении метод toJSON используется для сериализации данных обратно на то, что может понять сервер, - вы просто вызываете метод toJSON для каждого элемента модели, чтобы сериализовать его в формате, который будет распознан бэкэнд, Если вы посмотрите на код для Backbone.sync, вы увидите, что модель всегда сериализуется с использованием функции toJSON, если не передаются никакие пользовательские данные.

Дайте мне знать, если вам нужна более подробная информация, хотя я считаю, что вы сможете получить ее здесь!

Ответ 4

Да, вы можете использовать шаблон метода Factory для создания модели в базовых коллекциях. Предполагая, что структура данных, предложенная в @kpeel answer, вы можете определить

// Factory method
var CategoryOrItemFactory = function(data, options) {
    if (data.children) {
        return new Category(data, options);
    } else {
        return new Item(data, options);
    }
};

// Model definitions
var Item = Backbone.Model.extend();

var Category = Backbone.Model.extend({
    initialize: function() {
        this.children = new Children(this.get("children"));
    }
});

var Children = Backbone.Collection.extend({
    model: CategoryOrItemFactory
});

Затем вы создадите свой корневой элемент (категория) и создадите полную структуру данных:

// create the root item
var rootItem = new Category(rawData);

Доступ к дочерним элементам категории Category <Свойство children, например

  rootItem.children.get(2).get("name");

Ниже приведена jsFiddle с указанным выше кодом.