Backbone.js получает и устанавливает атрибут вложенного объекта - PullRequest
105 голосов
/ 15 июня 2011

У меня простой вопрос о функциях Backbone.js ' get и set .

1) С помощью приведенного ниже кода, как я могу "получить" или'set' obj1.myAttribute1 напрямую?

Другой вопрос:

2) В модели, кроме объекта defaults , где можно / нужно объявить другой объект моей моделиатрибуты, чтобы к ним можно было получить доступ через методы get и set Backbone?

var MyModel = Backbone.Model.extend({
    defaults: {
        obj1 : {
            "myAttribute1" : false,
            "myAttribute2" : true,
        }
    }
})

var MyView = Backbone.View.extend({
    myFunc: function(){
        console.log(this.model.get("obj1"));
        //returns the obj1 object
        //but how do I get obj1.myAttribute1 directly so that it returns false?
    }
});

Я знаю, что могу сделать:

this.model.get("obj1").myAttribute1;

но разве это хорошая практика?

Ответы [ 9 ]

144 голосов
/ 15 июня 2011

Хотя this.model.get("obj1").myAttribute1 хорошо, это немного проблематично, потому что тогда у вас может возникнуть соблазн сделать то же самое для набора, то есть

this.model.get("obj1").myAttribute1 = true;

Но если вы сделаете это, вы не будетеполучить преимущества моделей Backbone для myAttribute1, такие как события изменения или проверки.

Лучшим решением было бы никогда не вкладывать POJSO («простые старые объекты JavaScript») в ваши модели, а вместо этого вкладывать пользовательские классы моделей.,Таким образом, это будет выглядеть примерно так:

var Obj = Backbone.Model.extend({
    defaults: {
        myAttribute1: false,
        myAttribute2: true
    }
});

var MyModel = Backbone.Model.extend({
    initialize: function () {
        this.set("obj1", new Obj());
    }
});

Тогда код доступа будет

var x = this.model.get("obj1").get("myAttribute1");

, но что более важно, код установки будет

this.model.get("obj1").set({ myAttribute1: true });

, которыйбудет запускать соответствующие события изменения и тому подобное.Рабочий пример здесь: http://jsfiddle.net/g3U7j/

74 голосов
/ 05 ноября 2011

Я создал backbone-deep-model для этого - просто расширите Backbone.DeepModel вместо Backbone.Model, и затем вы сможете использовать пути для получения / установки вложенных атрибутов модели. Он также поддерживает изменения событий.

model.bind('change:user.name.first', function(){...});
model.set({'user.name.first': 'Eric'});
model.get('user.name.first'); //Eric
16 голосов
/ 20 февраля 2012

Решение Domenic будет работать, однако каждая новая MyModel будет указывать на один и тот же экземпляр Obj. Чтобы избежать этого, MyModel должен выглядеть так:

var MyModel = Backbone.Model.extend({
  initialize: function() {
     myDefaults = {
       obj1: new Obj()
     } 
     this.set(myDefaults);
  }
});

См. Полный ответ c3rin @ https://stackoverflow.com/a/6364480/1072653.

5 голосов
/ 06 марта 2013

Я использую этот подход.

Если у вас есть модель Backbone, подобная этой:

var nestedAttrModel = new Backbone.Model({
    a: {b: 1, c: 2}
});

Вы можете установить атрибут "a.b" с помощью:

var _a = _.omit(nestedAttrModel.get('a')); // from underscore.js
_a.b = 3;
nestedAttrModel.set('a', _a);

Теперь ваша модель будет иметь такие атрибуты, как:

{a: {b: 3, c: 2}}

с запущенным событием "change".

3 голосов
/ 22 августа 2016

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

//How model.obj1 looks like
obj1: {
    myAttribute1: false,
    myAttribute2: true,
    anotherNestedDict: {
        myAttribute3: false
    }
}

//Make a clone of it
var cloneOfObject1 = JSON.parse(JSON.stringify(this.model.get('obj1')));

//Let's day we want to change myAttribute1 to false and myAttribute3 to true
cloneOfObject1.myAttribute2 = false;
cloneOfObject1.anotherNestedDict.myAttribute3 = true;

//And now we set the whole dictionary
this.model.set('obj1', cloneOfObject1);

//Job done, happy birthday
2 голосов
/ 29 августа 2015

Решение, предложенное Домеником, имеет некоторые недостатки.Скажем, вы хотите прослушать событие «изменить».В этом случае метод 'initialize' не будет запущен и ваше пользовательское значение атрибута будет заменено на объект json с сервераВ моем проекте я столкнулся с этой проблемой.Мое решение переопределить метод 'set' для Model:

set: function(key, val, options) {
    if (typeof key === 'object') {
        var attrs = key;
        attrs.content = new module.BaseItem(attrs.content || {});
        attrs.children = new module.MenuItems(attrs.children || []);
    }

    return Backbone.Model.prototype.set.call(this, key, val, options);
}, 
2 голосов
/ 26 июля 2012

У меня была такая же проблема, как у @pagewil и @Benno с решением @ Domenic. Вместо этого я написал простой подкласс Backbone.Model, который решает проблему.

// Special model implementation that allows you to easily nest Backbone models as properties.
Backbone.NestedModel = Backbone.Model.extend({
    // Define Backbone models that are present in properties
    // Expected Format:
    // [{key: 'courses', model: Course}]
    models: [],

    set: function(key, value, options) {
        var attrs, attr, val;

        if (_.isObject(key) || key == null) {
            attrs = key;
            options = value;
        } else {
            attrs = {};
            attrs[key] = value;
        }

        _.each(this.models, function(item){
            if (_.isObject(attrs[item.key])) {
                attrs[item.key] = new item.model(attrs[item.key]);
            }
        },this);

        return Backbone.Model.prototype.set.call(this, attrs, options);
    }
});

var Obj = Backbone.Model.extend({
    defaults: {
        myAttribute1: false,
        myAttribute2: true
    }
});

var MyModel = Backbone.NestedModel.extend({
    defaults: {
        obj1: new Obj()
    },

    models: [{key: 'obj1', model: Obj}]
});

То, что NestedModel делает для вас, это позволяет им работать (что происходит, когда myModel устанавливается через данные JSON):

var myModel = new MyModel();
myModel.set({ obj1: { myAttribute1: 'abc', myAttribute2: 'xyz' } });
myModel.set('obj1', { myAttribute1: 123, myAttribute2: 456 });

Было бы легко автоматически генерировать список моделей при инициализации, но для меня это решение оказалось достаточно.

0 голосов
/ 21 июля 2015

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

backbone.linear может помочь вам.

0 голосов
/ 03 июня 2015

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

var MyModel = Backbone.Model.extend({
    defaults: {
        obj1 : {
            "myAttribute1" : false,
            "myAttribute2" : true,
        }
    },
    setObj1Attribute: function(name, value) {
        var obj1 = this.get('obj1');
        obj1[name] = value;
        this.set('obj1', obj1);
    }
})
...