Backbone.js iç içe nesne niteliği alma ve ayarlama


105

Ben basit bir backbone.js hakkında soru var olsun ve ayar işlevlerini.

1) Aşağıdaki kodla obj1.myAttribute1'i doğrudan nasıl 'alabilirim' veya 'ayarlayabilirim'?

Başka bir soru:

2) Modelde, defaultlar nesnesinin yanı sıra, modelimin diğer özelliklerini Backbone'un get ve set yöntemleri ile erişilebilecek şekilde nerede beyan edebilirim / belirtmeliyim?

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?
    }
});

Yapabileceğimi biliyorum:

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

ama bu iyi bir uygulama mı?


3
Bu soruya bir cevap olmasa da: defaults(bu durumda obj1 ) içinde bir nesneyi (referansla geçen herhangi bir şey ) belirtirken, aynı nesne modelin tüm örneklerinde paylaşılacaktır. Mevcut uygulama, defaultsvarsayılan olarak kullanılacak bir nesneyi döndüren bir işlev olarak tanımlamaktır . backbonejs.org/#Model-defaults (italik nota bakın)
Jonathan F

1
@JonathanF Yorumlar cevaplar için tasarlanmamıştır, bu yüzden beyana asla ihtiyaç
TJ

Yanıtlar:


144

İyi olsa this.model.get("obj1").myAttribute1da, biraz sorunlu çünkü o zaman set için aynı tür şeyleri yapmak isteyebilirsiniz, yani

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

Ancak bunu yaparsanız myAttribute1, değişiklik olayları veya doğrulama gibi Backbone modellerinin avantajlarından yararlanamazsınız .

Daha iyi bir çözüm, POJSO'ları ("düz eski JavaScript nesneleri") modellerinizde asla iç içe yerleştirmemek ve bunun yerine özel model sınıflarını iç içe yerleştirmektir. Yani şöyle bir şeye benzeyecektir:

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

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

Ardından erişim kodu

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

ama daha da önemlisi, ayar kodu

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

uygun değişiklik olaylarını ve benzerlerini tetikleyecek. Burada çalışma örneği: http://jsfiddle.net/g3U7j/


24
Bu cevaba, bu çözümün Demeter Yasası'nın yaygın ihlallerine yol açtığı tavsiyesini ekleyeceğim. İç içe nesneye gezinmeyi gizleyen kolaylık yöntemleri eklemeyi düşünürdüm. Temel olarak, arayanların modelin iç yapısını bilmesine gerek yoktur; ne de olsa değişebilir ve arayanlar daha akıllı olmamalıdır.
Bill Eisenhauer

7
Bunun benim için çalışmasını sağlayamazsın. Hata Uncaught TypeError: Object #<Object> has no method 'set'
atıyor

1
@ChristianNunciato, pagewil, Benno: Omurga modellerini Backbone modellerinin içine yerleştirmek olan yazının amacını kaçırmış gibisiniz. Düz nesneleri Backbone modellerinin içine yerleştirmeyin. Burada çalışma örneği: jsfiddle.net/g3U7j
Domenic

1
Backbone.js kodunu incelemedim, ancak benim testime göre, iç içe geçmiş bir özel Modeliniz varsa ve bir özelliğini set () ile değiştirirseniz, üst modeli bir 'değişiklik' olayını tetiklemeyecektir; Olayı kendim ateşlemek zorunda kaldım. Kodu gerçekten incelemeliyim, ama bu sizin de anlayışınız mı?
tom

2
@tom bu doğru. Backbone, modellerin özelliklerinin örnekleri olduğunda özel bir durum oluşturmaz Backbone.Modelve ardından sihirli olay köpürmesi yapmaya başlar.
Domenic

74

Bunun için derin omurga modeli yarattım - sadece Backbone.Model yerine Backbone.DeepModel'i genişletin ve ardından iç içe model niteliklerini almak / ayarlamak için yolları kullanabilirsiniz. Değişiklik olaylarını da korur.

model.bind('change:user.name.first', function(){...});
model.set({'user.name.first': 'Eric'});
model.get('user.name.first'); //Eric

1
Evet bu size bakarsanız, yaptığı API gibi bir örnek var//You can use index notation to fetch from arrays console.log(model.get('otherSpies.0.name')) //'Lana'
Tevhîd

Harika çalışıyor! Peki örneğinizdeki 2. satır, virgül yerine iki nokta üst üste kullanmak mı gerektiriyor?
mariachi

16

Domenic'in çözümü işe yarayacak, ancak her yeni MyModel aynı Obj örneğini gösterecektir. Bunu önlemek için MyModel şöyle görünmelidir:

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

Tam bir açıklama için c3rin'in cevabına @ https://stackoverflow.com/a/6364480/1072653 bakın.


1
Gelecekteki okuyucular için cevabım, Rusty'nin en iyi cevabını içerecek şekilde güncellendi.
Domenic

2
Soran kişi bunu kabul edilen yanıt olarak işaretlemelidir. Domenic'inki harika bir başlangıç, ancak bu onunla ilgili bir sorunu çözüyor.
Jon Raasch

5

Bu yaklaşımı kullanıyorum.

Bunun gibi bir Omurga modeliniz varsa:

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

"Ab" özelliğini şu şekilde ayarlayabilirsiniz:

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

Artık modeliniz aşağıdaki gibi özelliklere sahip olacak:

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

"değiştirme" olayı tetiklendi.


1
Bundan emin misin? bu benim için çalışmıyor. meta2= m.get('x'); meta2.id=110; m.set('x', meta2). Bu benim için herhangi bir değişiklik olayını tetiklemiyor :(
HungryCoder

1
Özniteliği klonladığımda çalıştığını görüyorum _.clone(m.get('x')). teşekkürler
HungryCoder

Teşekkürler @ HungryCoder klonlandığında benim için de çalıştı. Omurga setting, bulunduğunuz nesneyi gettingayarlanan zamanda bulunduğunuz nesne ile karşılaştırmalıdır . Yani klonlamazsanız, o zaman iki nesne, o zaman karşılaştırılan iki nesne ayarlanan zamanda tamamen aynıdır.
Derek Dahmer

Dize ve sayı ilkellerinin aksine, nesnelerin referans olarak aktarıldığını ve değiştirilebilir olduğunu unutmayın. Backbone'un set ve yapıcı yöntemleri, argüman olarak iletilen herhangi bir nesne referansının sığ bir klonunu dener. Bu nesnenin özelliklerindeki diğer nesnelere yapılan başvurular klonlanmaz. Ayarladığınızda ve geri aldığınızda referans aynıdır, bu da modeli bir değişikliği tetiklemeden değiştirebileceğiniz anlamına gelir.
niall.campbell

3

Henüz kimsenin aklına gelmemiş bir çözüm var. Muhtemelen istemediğiniz bir üçüncü taraf kitaplığı kullanmıyorsanız, iç içe geçmiş öznitelikleri doğrudan ayarlayamazsınız. Bununla birlikte, yapabileceğiniz şey, orijinal sözlüğün bir klonunu yapmak, yuvalanmış özelliği oraya ayarlamak ve ardından sözlüğün tamamını ayarlamaktır. Kekin parçası.

//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

@Pagewil ve @Benno'nun @ Domenic'in çözümüyle aynı sorunu yaşadım. Cevabım bunun yerine sorunu çözen basit bir Backbone.Model alt sınıfı yazmaktı.

// 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'in sizin için yaptığı şey, bunların çalışmasına izin vermektir (myModel, JSON verileri aracılığıyla ayarlandığında böyle olur):

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

Başlangıçta model listesini otomatik olarak oluşturmak kolay olurdu, ancak bu çözüm benim için yeterince iyiydi.


2

Domenic tarafından önerilen çözümün bazı dezavantajları vardır. 'Değişim' olayını dinlemek istediğinizi varsayalım. Bu durumda 'initialize' yöntemi tetiklenmeyecek ve öznitelik için özel değeriniz sunucudaki json nesnesiyle değiştirilecektir. Projemde bu sorunla karşılaştım. Modelin 'set' yöntemini geçersiz kılma çözümüm:

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);
}, 

0

Bazı durumlarda yuvalanmış Nesne öznitelikleri yerine Omurga modellerini kullanmak mantıklı olsa da, Domenic'in bahsettiği gibi daha basit durumlarda modelde bir ayarlayıcı işlevi oluşturabilirsiniz:

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);
    }
})

0

İç içe geçmiş yapıya sahip nesne gerektiren arka uç ile etkileşimde bulunursanız. Ancak omurga ile lineer yapı ile çalışmak daha kolaydır.

backbone.linear size yardımcı olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.