ventajas tutorial react example español ejemplos desventajas backbone javascript backbone.js backbone-model

javascript - react - backbone tutorial español



Modelos anidados en Backbone.js, cómo abordar (12)

Estoy publicando este código como un ejemplo de la sugerencia de Peter Lyon para redefinir el análisis. Tenía la misma pregunta y esto funcionó para mí (con un servidor de Rails). Este código está escrito en Coffeescript. Hice algunas cosas explícitas para las personas que no están familiarizadas con esto.

class AppName.Collections.PostsCollection extends Backbone.Collection model: AppName.Models.Post url: ''/posts'' ... # parse: redefined to allow for nested models parse: (response) -> # function definition # convert each comment attribute into a CommentsCollection if _.isArray response _.each response, (obj) -> obj.comments = new AppName.Collections.CommentsCollection obj.comments else response.comments = new AppName.Collections.CommentsCollection response.comments return response

o, en JS

parse: function(response) { if (_.isArray(response)) { return _.each(response, function(obj) { return obj.comments = new AppName.Collections.CommentsCollection(obj.comments); }); } else { response.comments = new AppName.Collections.CommentsCollection(response.comments); } return response; };

Tengo el siguiente JSON provisto por un servidor. Con esto, quiero crear un modelo con un modelo anidado. No estoy seguro de cuál es la forma de lograr esto.

//json [{ name : "example", layout : { x : 100, y : 100, } }]

Quiero que estos se conviertan en dos modelos de red troncal anidados con la siguiente estructura:

// structure Image Layout ...

Así que defino el modelo de diseño así:

var Layout = Backbone.Model.extend({});

Pero, ¿cuál de las dos (si las hay) técnicas a continuación debería usar para definir el modelo de imagen? A o B a continuación?

UN

var Image = Backbone.Model.extend({ initialize: function() { this.set({ ''layout'' : new Layout(this.get(''layout'')) }) } });

o, B

var Image = Backbone.Model.extend({ initialize: function() { this.layout = new Layout( this.get(''layout'') ); } });


Me doy cuenta de que llego tarde a esta fiesta, pero recientemente lanzamos un complemento para tratar exactamente este escenario. Se llama backbone-nestify .

Entonces su modelo anidado permanece sin cambios:

var Layout = Backbone.Model.extend({...});

Luego use el complemento cuando defina el modelo que lo contiene (usando Underscore.extend ):

var spec = { layout: Layout }; var Image = Backbone.Model.extend(_.extend({ // ... }, nestify(spec));

Después de eso, suponiendo que tienes un modelo m que es una instancia de Image , y que has establecido el JSON a partir de la pregunta en m , puedes hacer:

m.get("layout"); //returns the nested instance of Layout m.get("layout|x"); //returns 100 m.set("layout|x", 50); m.get("layout|x"); //returns 50


Me gustaría ir con la Opción B si quiere mantener las cosas simples.

Otra buena opción sería usar github.com/PaulUithol/Backbone-relational . Simplemente definirías algo como:

var Image = Backbone.Model.extend({ relations: [ { type: Backbone.HasOne, key: ''layout'', relatedModel: ''Layout'' } ] });


No estoy seguro de que Backbone tenga una forma recomendada de hacerlo. ¿El objeto de diseño tiene su propia identificación y registro en la base de datos back-end? Si es así, puedes hacer que sea tu propio Modelo como lo has hecho. De lo contrario, puede dejarlo como un documento anidado, solo asegúrese de convertirlo a JSON y de manera adecuada en los métodos de save y parse . Si terminas tomando un enfoque como este, creo que tu ejemplo A es más consistente con la red troncal ya que el set actualizará apropiadamente los attributes , pero nuevamente no estoy seguro de qué es lo que Backbone hace con los modelos anidados de forma predeterminada. Es probable que necesite algún código personalizado para manejar esto.


Si no desea agregar aún otro marco, podría considerar crear una clase base con un set reemplazado y toJSON y usarlo así:

// Declaration window.app.viewer.Model.GallerySection = window.app.Model.BaseModel.extend({ nestedTypes: { background: window.app.viewer.Model.Image, images: window.app.viewer.Collection.MediaCollection } }); // Usage var gallery = new window.app.viewer.Model.GallerySection({ background: { url: ''http://example.com/example.jpg'' }, images: [ { url: ''http://example.com/1.jpg'' }, { url: ''http://example.com/2.jpg'' }, { url: ''http://example.com/3.jpg'' } ], title: ''Wow'' }); // (fetch will work equally well) console.log(gallery.get(''background'')); // window.app.viewer.Model.Image console.log(gallery.get(''images'')); // window.app.viewer.Collection.MediaCollection console.log(gallery.get(''title'')); // plain string

Necesitarás BaseModel de esta respuesta (disponible, si te apetece, como esencia ).


También tenemos este problema y un trabajador del equipo ha implementado un complemento llamado backbone-nested-attributes.

El uso es muy simple. Ejemplo:

var Tree = Backbone.Model.extend({ relations: [ { key: ''fruits'', relatedModel: function () { return Fruit } } ] }) var Fruit = Backbone.Model.extend({ })

Con esto, el modelo Tree puede acceder a las frutas:

tree.get(''fruits'')

Puedes ver más información aquí:

https://github.com/dtmtec/backbone-nested-attributes


Tengo el mismo problema mientras escribo mi aplicación Backbone. Tener que lidiar con modelos integrados / anidados. Hice algunos ajustes que pensé que era una solución bastante elegante.

Sí, puedes modificar el método de análisis para cambiar los atributos en el objeto, pero todo eso es realmente un código IMO poco sostenible, y se siente más como un hack que como una solución.

Esto es lo que sugiero para su ejemplo:

Primero defina su Modelo de diseño como tal.

var layoutModel = Backbone.Model.extend({});

Entonces aquí está tu imagen Modelo:

var imageModel = Backbone.Model.extend({ model: { layout: layoutModel, }, parse: function(response){ for(var key in this.model) { var embeddedClass = this.model[key]; var embeddedData = response[key]; response[key] = new embeddedClass(embeddedData, {parse:true}); } return response; } });

Tenga en cuenta que no he alterado el modelo en sí, sino que simplemente paso el objeto deseado desde el método de análisis.

Esto debería garantizar la estructura del modelo anidado cuando estás leyendo desde el servidor. Ahora, notará que el guardado o la configuración no se manejan aquí porque creo que tiene sentido que configure el modelo anidado explícitamente utilizando el modelo adecuado.

Al igual que:

image.set({layout : new Layout({x: 100, y: 100})})

También tenga en cuenta que en realidad está invocando el método de análisis en su modelo anidado al llamar:

new embeddedClass(embeddedData, {parse:true});

Puede definir tantos modelos anidados en el campo de model como necesite.

Por supuesto, si quiere ir tan lejos como guardar el modelo anidado en su propia tabla. Esto no sería suficiente. Pero en el caso de leer y guardar el objeto como un todo, esta solución debería ser suficiente.


Tuve el mismo problema y he estado experimentando con el código en la respuesta de rycfung , que es una gran sugerencia.
Sin embargo, si no desea set los modelos anidados directamente, o no quiere pasar constantemente {parse: true} en las options , otro enfoque sería redefinir el propio set .

En Backbone 1.0.0 , se llama set a constructor , unset , clear , fetch y save .

Considere el siguiente supermodelo , para todos los modelos que necesitan anidar modelos y / o colecciones.

/** Compound supermodel */ var CompoundModel = Backbone.Model.extend({ /** Override with: key = attribute, value = Model / Collection */ model: {}, /** Override default setter, to create nested models. */ set: function(key, val, options) { var attrs, prev; if (key == null) { return this; } // Handle both `"key", value` and `{key: value}` -style arguments. if (typeof key === ''object'') { attrs = key; options = val; } else { (attrs = {})[key] = val; } // Run validation. if (options) { options.validate = true; } else { options = { validate: true }; } // For each `set` attribute, apply the respective nested model. if (!options.unset) { for (key in attrs) { if (key in this.model) { if (!(attrs[key] instanceof this.model[key])) { attrs[key] = new this.model[key](attrs[key]); } } } } Backbone.Model.prototype.set.call(this, attrs, options); if (!(attrs = this.changedAttributes())) { return this; } // Bind new nested models and unbind previous nested models. for (key in attrs) { if (key in this.model) { if (prev = this.previous(key)) { this._unsetModel(key, prev); } if (!options.unset) { this._setModel(key, attrs[key]); } } } return this; }, /** Callback for `set` nested models. * Receives: * (String) key: the key on which the model is `set`. * (Object) model: the `set` nested model. */ _setModel: function (key, model) {}, /** Callback for `unset` nested models. * Receives: * (String) key: the key on which the model is `unset`. * (Object) model: the `unset` nested model. */ _unsetModel: function (key, model) {} });

Observe que el model , _setModel y _unsetModel se dejan en blanco a propósito. En este nivel de abstracción, probablemente no pueda definir ninguna acción razonable para las devoluciones de llamada. Sin embargo, es posible que desee sobrescribirlos en los submodelos que extienden CompoundModel .
Esas devoluciones de llamada son útiles, por ejemplo, para vincular oyentes y propagar eventos de change .

Ejemplo:

var Layout = Backbone.Model.extend({}); var Image = CompoundModel.extend({ defaults: function () { return { name: "example", layout: { x: 0, y: 0 } }; }, /** We need to override this, to define the nested model. */ model: { layout: Layout }, initialize: function () { _.bindAll(this, "_propagateChange"); }, /** Callback to propagate "change" events. */ _propagateChange: function () { this.trigger("change:layout", this, this.get("layout"), null); this.trigger("change", this, null); }, /** We override this callback to bind the listener. * This is called when a Layout is set. */ _setModel: function (key, model) { if (key !== "layout") { return false; } this.listenTo(model, "change", this._propagateChange); }, /** We override this callback to unbind the listener. * This is called when a Layout is unset, or overwritten. */ _unsetModel: function (key, model) { if (key !== "layout") { return false; } this.stopListening(); } });

Con esto, tiene la creación automática de modelos anidados y la propagación de eventos. El uso de la muestra también se proporciona y prueba:

function logStringified (obj) { console.log(JSON.stringify(obj)); } // Create an image with the default attributes. // Note that a Layout model is created too, // since we have a default value for "layout". var img = new Image(); logStringified(img); // Log the image everytime a "change" is fired. img.on("change", logStringified); // Creates the nested model with the given attributes. img.set("layout", { x: 100, y: 100 }); // Writing on the layout propagates "change" to the image. // This makes the image also fire a "change", because of `_propagateChange`. img.get("layout").set("x", 50); // You may also set model instances yourself. img.set("layout", new Layout({ x: 100, y: 100 }));

Salida:

{"name":"example","layout":{"x":0,"y":0}} {"name":"example","layout":{"x":100,"y":100}} {"name":"example","layout":{"x":50,"y":100}} {"name":"example","layout":{"x":100,"y":100}}


Usar Backbone.AssociatedModel de Backbone-associations :

var Layout = Backbone.AssociatedModel.extend({ defaults : { x : 0, y : 0 } }); var Image = Backbone.AssociatedModel.extend({ relations : [ type: Backbone.One, key : ''layout'', relatedModel : Layout ], defaults : { name : '''', layout : null } });


Usar formas de red troncal

Es compatible con formularios anidados, modelos y toJSON. TODOS LOS ANIMADOS

var Address = Backbone.Model.extend({ schema: { street: ''Text'' }, defaults: { street: "Arteaga" } }); var User = Backbone.Model.extend({ schema: { title: { type: ''Select'', options: [''Mr'', ''Mrs'', ''Ms''] }, name: ''Text'', email: { validators: [''required'', ''email''] }, birthday: ''Date'', password: ''Password'', address: { type: ''NestedModel'', model: Address }, notes: { type: ''List'', itemType: ''Text'' } }, constructor: function(){ Backbone.Model.apply(this, arguments); }, defaults: { email: "[email protected]" } }); var user = new User(); user.set({address: {street: "my other street"}}); console.log(user.toJSON()["address"]["street"]) //=> my other street var form = new Backbone.Form({ model: user }).render(); $(''body'').append(form.el);


Uso el complemento Backbone DeepModel para modelos y atributos anidados.

https://github.com/powmedia/backbone-deep-model

Puede enlazar para cambiar los eventos ''n levels deep. por ejemplo: model.on(''change:example.nestedmodel.attribute'', this.myFunction);


Versión de CoffeeScript de la bella respuesta de rycfung :

class ImageModel extends Backbone.Model model: { layout: LayoutModel } parse: (response) => for propName,propModel of @model response[propName] = new propModel( response[propName], {parse:true, parentModel:this} ) return response

¿No es eso dulce? ;)