Различные типы моделей в одной коллекции Backbone

Backbone 1.1.2
Underscore 1.7.0
jQuery 1.11.1

У меня есть одна коллекция, содержащая сообщения. Мои сообщения могут быть разных типов (и конечные точки в API разные для каждого типа, но у меня есть конечная точка, которая позволяет мне сделать один запрос и получить все сообщения)

Когда Collection.fetch() мне нужно определить, какую модель использовать при заполнении коллекции на основе существующих свойств.

Я пробовал, как было предложено здесь: Коллекция Backbone.js нескольких подклассов модели, а также документацию по магистрали backbonejs.org

Мой код выглядит так

model: function (attr, options) {
    if(attr.hasOwnProperty('prop')){
        return new PropModel(attr,options);
    }
    else if(attr.hasOwnProperty('another_prop')){
        new AnotherPropModel(attr,options);
    }
},

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

Правильно ли я обрабатываю это, есть ли другой способ сделать это?

---ОБНОВИТЬ----

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

parse: function (resp, options) {
    _.each(resp, _.bind(function (r) {
        console.log(this);
        if(r.hasOwnProperty('prop')){
            this.add(new PropModel(r));
        }else{
            this.add(new AnotherPropModel(r));
        }
    },this));
}

person Beyerz    schedule 30.12.2014    source источник
comment
Есть ли принципиальная разница между моделями? Или это просто URL?   -  person anAgent    schedule 30.12.2014
comment
@anAgent Атрибуты для каждого разные, поэтому я хотел бы приводить типы для каждой модели при рендеринге моего шаблона. Я думаю, я мог бы сделать ту же логику в шаблоне, но я предпочитаю держать логику вне шаблона, чтобы я мог легко переключать механизмы шаблонов, если это необходимо.   -  person Beyerz    schedule 30.12.2014
comment
Также URL-адрес важен для меня, поскольку атрибуты разные. Несмотря на то, что сообщения могут быть сгруппированы, они также независимы в других областях моей системы.   -  person Beyerz    schedule 30.12.2014
comment
похоже, вы на правильном пути - при необходимости измените URL-адрес выборки коллекции, а затем в этой функции вам просто нужно вернуть функцию модели (не обновленную) - if (attr.modelType === 'mailItem') { return MailItemModel; }   -  person Dominic    schedule 30.12.2014
comment
@DominicTobias В атрибуте у меня нет типа модели. У меня есть объекты массива, проанализированные из Json.   -  person Beyerz    schedule 30.12.2014
comment
@Beyerz сервер возвращает массив объектов json, и в этой структуре (которая является attrs) должна быть информация, описывающая, какую модель вы хотите для нее   -  person Dominic    schedule 30.12.2014
comment
@DominicTobias Я понимаю, о чем вы говорите, я попробовал r.hasOwnProperty('prop'), чтобы разделить их и определить правильную модель для каждой в атрибутах.   -  person Beyerz    schedule 30.12.2014
comment
Чтобы синтаксический анализ работал, вам все равно нужно вернуть ответ.   -  person anAgent    schedule 31.12.2014


Ответы (2)


Таким образом, решение было сочетанием использования функции модели и возврата.

Вот объяснение:

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

parse: function (resp, options) {
    return resp;
}

В моем случае сервер возвращал объект объекта как

{{1:data},{2:data}}

Во-первых, это странно и, очевидно, должно быть решено. ВАЖНЫЙ МОМЕНТ: когда магистраль оценивает ответ, возвращаемый синтаксическим анализом, она должна решить, где остановиться для каждой модели, как в том, что определяет новую модель.

Backbone рассматривает объекты как единую модель, и, как и в моем случае, у меня был один большой объект, я получал одну большую модель... вот почему аргумент attrs в функции модели был одной большой мешаниной данных.

Поэтому я просто изменил свой ответ в функции синтаксического анализа и вуаля!! все в функции модели работало так, как ожидалось:

Вот код:

model: function (attr, options) {
    if(attr.hasOwnProperty('prop')){
        return new PropModel(attr,options);
    }
    else if (attr.hasOwnProperty('anotherProp')){
        return new AnotherPropModel(attr,options);
    }
},

parse: function (resp, options) {
    var response = [];
    _.each(resp, _.bind(function (r) {
        response.push(r);
    },this));
    return response;
}

Я уверен, что есть лучший способ преобразовать объект в массив, но пока это работает, и я снова улыбаюсь!!

Эта статья привела меня к решению: коллекция кукол

person Beyerz    schedule 30.12.2014
comment
Просто комментарий о том, что вам не нужно использовать _.bind, поскольку третий аргумент _.each является контекстом. Во-вторых, вы ничего не вызываете за пределами _.each, поэтому в этом нет необходимости. - person anAgent; 31.12.2014
comment
ты прав!! Я удалил _.bind, думаю, у меня просто появилась привычка терять свою область видимости, поэтому я склонен привязывать функции обратного вызова по привычке. - person Beyerz; 03.01.2015

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

Пример JSON

"[{"id":1,"Type":"Person"},{"id":2,"Type":"Business"}]"

Пример модели

var Person = Backbone.Model.extend({

    keyTypes: {
        Person: 'Person',
        Business: 'Business'
    },

    url: function() {
        // we are assuming only two types. Change logic if there are three or more types.
        return this.get('Type') === this.keyTypes.Person ? '/api/people' : '/api/businesss';
    }

});

Подборка

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

Просмотреть

var View = Backbone.View.extend({

    initialize: function() {
        this.collection = new Collection()
            .on('fetch', this.render, this);
    },

    bootstrap: function() {
        this.collection.fetch();
    }

    render: function() {
        this.$el.html(_.template({
            models: this.collection.toJSON()
        }));
    }
})

** !! Обновлять !! **

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

parse: function (data, options) {

    var models = [];
    _.each(data, function (entity) {
        // this is IE8 Safe....
        var model = Object.prototype.hasOwnProperty.call(entity,'prop') ? PropModel : AnotherPropModel;
        models.push(new model(entity));    
    });

    return models;
}
person anAgent    schedule 30.12.2014
comment
Я вижу, как это разрешит несколько конечных точек URL-адресов, но я до сих пор не понимаю, как это определяет модели для меня, чтобы я мог приводить типы? - person Beyerz; 30.12.2014
comment
Он отвечает на поиск приведения типов, поэтому я согласен с тем, что он не отвечает вашему точному вопросу. Я думаю, что если вы начнете приведение типов, вы начнете сталкиваться с другими долгосрочными проблемами, а также вам будет сложнее тестировать. - person anAgent; 31.12.2014
comment
На самом деле я фанат приведения типов, поскольку это требует понимания и позволяет быстро ошибаться. Ваше обновление имеет смысл, я забыл упомянуть, что работаю над расширением для Chrome ... так что на данный момент не слишком беспокоюсь о совместимости с разными браузерами. - person Beyerz; 03.01.2015