Backbone.js ottiene e imposta l’attributo dell’object nidificato

Ho una semplice domanda su come ottenere e impostare le funzioni di Backbone.js.

1) Con il codice qui sotto, come posso “ottenere” o “impostare” obj1.myAttribute1 direttamente?

Un’altra domanda:

2) Nel modello, a parte l’object default , dove posso / dovrei dichiarare gli altri attributi del mio modello, in modo tale che possano essere accessibili tramite i metodi get e set di 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? } }); 

So che posso fare:

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

ma è una buona pratica?

Mentre this.model.get("obj1").myAttribute1 va bene, è un po ‘problematico perché allora potresti essere tentato di fare lo stesso tipo di cosa per set, cioè

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

Ma se lo fai, non otterrai i benefici dei modelli Backbone per myAttribute1 , come gli eventi di modifica o la convalida.

Una soluzione migliore sarebbe quella di non annidare mai i POJSO (“semplici vecchi oggetti JavaScript”) nei modelli, e invece nidificare le classi di modelli personalizzati. Quindi sarebbe simile a questo:

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

Quindi il codice di accesso sarebbe

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

ma ancora più importante sarebbe il codice di impostazione

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

che genererà eventi di cambiamento appropriati e simili. Esempio di lavoro qui: http://jsfiddle.net/g3U7j/

Ho creato backbone-deep-model per questo – basta estendere Backbone.DeepModel invece di Backbone.Model e puoi quindi utilizzare i percorsi per ottenere / impostare gli attributi del modello annidato. Mantiene anche gli eventi di cambiamento.

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

La soluzione di Domenic funzionerà comunque ogni nuovo MyModel punta alla stessa istanza di Obj. Per evitare ciò, MyModel dovrebbe apparire come:

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

Vedi la risposta di c3rin @ https://stackoverflow.com/a/6364480/1072653 per una spiegazione completa.

Io uso questo approccio.

Se hai un modello Backbone come questo:

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

È ansible impostare l’attributo “ab” con:

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

Ora il tuo modello avrà attributi come:

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

con l’evento “change” triggersto.

Ho avuto lo stesso problema @pagewil e @Benno ha avuto con la soluzione di @ Domenic. La mia risposta è stata invece di scrivere una semplice sottoclass di Backbone.Model che risolve il problema.

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

Ciò che NestedModel fa per te è consentire a questi di funzionare (che è ciò che accade quando myModel viene impostato tramite dati JSON):

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

Sarebbe facile generare automaticamente la lista dei modelli in fase di inizializzazione, ma questa soluzione è stata abbastanza buona per me.

La soluzione proposta da Domenic presenta alcuni inconvenienti. Dì che vuoi ascoltare l’evento ‘cambia’. In tal caso, il metodo ‘initialize’ non verrà triggersto e il valore personalizzato per l’attributo verrà sostituito dall’object json dal server. Nel mio progetto ho affrontato questo problema. La mia soluzione per sovrascrivere il metodo ‘set’ del Modello:

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

C’è una soluzione a cui nessuno ha ancora pensato e che è molto utile. In effetti, non è ansible impostare direttamente attributi nidificati, a meno che non si utilizzi una libreria di terze parti che probabilmente non si desidera. Tuttavia, ciò che puoi fare è creare un clone del dizionario originale, impostare la proprietà nidificata e impostare l’intero dizionario. Pezzo di torta.

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

Mentre in alcuni casi l’uso di modelli Backbone invece di attributi Object annidati ha senso, come indicato da Domenic, in casi più semplici è ansible creare una funzione setter nel modello:

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

Se interagisci con il back-end, che richiede l’object con la struttura di nidificazione. Ma con il backbone più facile da lavorare con la struttura lineare.

backbone.linear può aiutarti.