javascript - objetos - Backbone.js obtiene y establece el atributo de objeto anidado
recorrer array de objetos javascript (9)
Tengo una simple pregunta acerca de las funciones de obtención y configuración de Backbone.js.
1) Con el siguiente código, ¿cómo puedo ''obtener'' o ''establecer'' obj1.myAttribute1 directamente?
Otra pregunta:
2) En el Modelo, además del objeto predeterminado , ¿dónde puedo / debo declarar los otros atributos de mi modelo, de modo que se pueda acceder a ellos a través de los métodos get y set de 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?
}
});
Sé que puedo hacer:
this.model.get("obj1").myAttribute1;
pero es esa buena práctica?
Creé backbone-deep-model para esto, solo extiendo Backbone.DeepModel en lugar de Backbone.Model y luego puede usar rutas para obtener / establecer atributos de modelos anidados. Mantiene eventos de cambio también.
model.bind(''change:user.name.first'', function(){...});
model.set({''user.name.first'': ''Eric''});
model.get(''user.name.first''); //Eric
Hay una solución que nadie pensó aún y que es mucho para usar. De hecho, no puede establecer atributos anidados directamente, a menos que use una biblioteca de terceros que probablemente no desee. Sin embargo, lo que puede hacer es hacer una copia del diccionario original, establecer la propiedad anidada allí y luego configurar ese diccionario completo. Pedazo de pastel.
//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
La solución de Domenic funcionará; sin embargo, cada nuevo modelo MyModel apuntará a la misma instancia de Obj. Para evitar esto, MyModel debería verse así:
var MyModel = Backbone.Model.extend({
initialize: function() {
myDefaults = {
obj1: new Obj()
}
this.set(myDefaults);
}
});
Consulte la respuesta de c3rin @ https://.com/a/6364480/1072653 para obtener una explicación completa.
La solución propuesta por Domenic tiene algunos inconvenientes. Digamos que quieres escuchar el evento ''cambiar''. En ese caso, el método de "inicialización" no se activará y su valor personalizado para el atributo se reemplazará por el objeto json del servidor. En mi proyecto me enfrenté con este problema. Mi solución para anular el método ''set'' de 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);
},
Si bien this.model.get("obj1").myAttribute1
está bien, es un poco problemático porque entonces podría estar tentado a hacer el mismo tipo de cosas para el conjunto, es decir,
this.model.get("obj1").myAttribute1 = true;
Pero si hace esto, no obtendrá los beneficios de los modelos Backbone para myAttribute1
, como eventos de cambio o validación.
Una mejor solución sería nunca anidar POJSO ("objetos simples de JavaScript antiguos") en sus modelos, y en su lugar anidar clases de modelos personalizados. Entonces se vería algo como esto:
var Obj = Backbone.Model.extend({
defaults: {
myAttribute1: false,
myAttribute2: true
}
});
var MyModel = Backbone.Model.extend({
initialize: function () {
this.set("obj1", new Obj());
}
});
Entonces el código de acceso sería
var x = this.model.get("obj1").get("myAttribute1");
pero lo más importante es que el código de configuración sería
this.model.get("obj1").set({ myAttribute1: true });
que disparará eventos de cambio apropiados y similares. Ejemplo de trabajo aquí: http://jsfiddle.net/g3U7j/
Si bien en algunos casos el uso de modelos de Backbone en lugar de los atributos de Object anidados tiene sentido como se mencionó en Domenic, en casos más simples podría crear una función setter en el modelo:
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);
}
})
Si interactúa con el servidor, que requiere un objeto con estructura de anidamiento. Pero con la columna vertebral más fácil de trabajar con estructura lineal.
backbone.linear puede ayudarte.
Tuve el mismo problema que @pagewil y @Benno con la solución de @Domenic. Mi respuesta fue escribir una subclase simple de Backbone. Modelo que soluciona el 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}]
});
Lo que NestedModel hace por usted es permitir que esto funcione (que es lo que sucede cuando myModel se configura a través de datos JSON):
var myModel = new MyModel();
myModel.set({ obj1: { myAttribute1: ''abc'', myAttribute2: ''xyz'' } });
myModel.set(''obj1'', { myAttribute1: 123, myAttribute2: 456 });
Sería fácil generar la lista de modelos automáticamente en la inicialización, pero esta solución fue lo suficientemente buena para mí.
Yo uso este enfoque.
Si tienes un modelo Backbone como este:
var nestedAttrModel = new Backbone.Model({
a: {b: 1, c: 2}
});
Puede establecer el atributo "ab" con:
var _a = _.omit(nestedAttrModel.get(''a'')); // from underscore.js
_a.b = 3;
nestedAttrModel.set(''a'', _a);
Ahora su modelo tendrá atributos como:
{a: {b: 3, c: 2}}
con el evento de "cambio" disparado.