backbone.js

Estableciendo id y className dinĂ¡micamente en vistas de Backbone.js



(8)

Estoy en proceso de aprender y usar Backbone.js.

Tengo un modelo de artículo y una vista de artículo correspondiente. Cada instancia de modelo tiene los atributos item_class y item_id, en los que quiero que se reflejen los atributos ''id'' y ''class'' de la vista correspondiente. ¿Cuál es la forma correcta de lograr esto?

Ejemplo:

var ItemModel = Backbone.Model.extend({ }); var item1 = new ItemModel({item_class: "nice", item_id: "id1"}); var item2 = new ItemModel({item_class: "sad", item_id: "id2"}); var ItemView = Backbone.View.extend({ });

¿Cómo debo implementar la vista para que las vistas de las vistas se traduzcan en:

<div id="id1" class="nice"></div> <div id="id2" class="sad"> </div>

En la mayoría de los ejemplos que he visto, el elemento de la vista sirve como un elemento contenedor sin sentido dentro del cual se debe escribir manualmente el código ''semántico''.

var ItemView = Backbone.View.extend({ tagName: "div", // I know it''s the default... render: function() { $(this.el).html("<div id="id1" class="nice"> Some stuff </div>"); } });

Entonces, cuando se procesa, uno obtiene

<div> <!-- el wrapper --> <div id="id1" class="nice"> Some stuff </div> </div>

Pero esto parece un desperdicio, ¿por qué tener el div externo? ¡Quiero que el el traduzca directamente al div interno!


Resumen: atributos de vista establecidos dinámicamente con datos de modelo

http://jsfiddle.net/5wd0ma8b/

// View class with `attributes` method var View = Backbone.View.extend( { attributes : function () { // Return model data return { class : this.model.get( ''item_class'' ), id : this.model.get( ''item_id'' ) }; } // attributes } ); // Pass model to view constructor var item = new View( { model : new Backbone.Model( { item_class : "nice", item_id : "id1" } ) } );

  • Este ejemplo supone que está permitiendo que Backbone genere un elemento DOM para usted.

  • El método de attributes se invoca después de establecer las propiedades pasadas al constructor de vista (en este caso, model ), lo que le permite establecer dinámicamente los atributos con los datos del modelo antes de que Backbone cree el .

  • En contraste con algunas de las otras respuestas: no codifica los valores de los atributos en la clase de vista, los establece dinámicamente a partir de los datos del modelo; no espera hasta render() para establecer attr vals; no establece repetidamente atributos en cada llamada a render() ; no establece innecesariamente valores de atributo en el elemento DOM.

  • Tenga en cuenta que si establece la clase al llamar Backbone.View.extend o un constructor de vista (por ejemplo, new Backbone.View ), debe usar el nombre de propiedad DOM, className , pero si lo establece a través de los attributes hash / method (como en este ejemplo) tienes que usar el nombre del atributo, class .

  • A partir de Backbone 0.9.9:

    Al declarar una Vista ... el , tagName , id y className ahora pueden definirse como funciones, si desea que sus valores se determinen en tiempo de ejecución.

    Menciono esto en caso de que haya una situación en la que eso sería útil como alternativa al uso de un método de attributes como se ilustra.

Usando un elemento existente

Si está utilizando un elemento existente (por ejemplo, pasando el al constructor de vista) ...

var item = new View( { el : some_el } );

... entonces los attributes no se aplicarán al elemento. Si los atributos deseados aún no están configurados en el elemento, o no desea duplicar esos datos en su clase de vista y en otra ubicación, entonces puede agregar un método de initialize al constructor de vista que aplique attributes a el . Algo como esto (usando jQuery.attr ):

View.prototype.initialize = function ( options ) { this.$el.attr( _.result( this, ''attributes'' ) ); };

Uso de el , renderizado, evitando la envoltura

En la mayoría de los ejemplos que he visto, el elemento de la vista sirve como un elemento contenedor sin sentido dentro del cual se debe escribir manualmente el código ''semántico''.

No hay ninguna razón por la que view.el necesite ser "un elemento de envoltura sin sentido". De hecho, eso a menudo rompería la estructura DOM. Si una clase de vista representa un elemento <li> , por ejemplo, necesita representarse como <li> , representarlo como <div> o cualquier otro elemento rompería el modelo de contenido. Es probable que desee centrarse en configurar correctamente el elemento de su vista (utilizando propiedades como tagName , className e id ) y luego presentar su contenido a partir de entonces.

Las opciones para que tus objetos de la vista Backbone interactúen con el DOM están abiertos. Hay 2 escenarios iniciales básicos:

  • Puede adjuntar un elemento DOM existente a una vista Backbone.

  • Puede permitir que Backbone cree un nuevo elemento que esté desconectado del documento y luego, de alguna manera, insertarlo en el documento.

Hay varias maneras en que puede generar el contenido para el elemento (establezca una cadena literal, como en su ejemplo, use una biblioteca de plantillas como Mustache, Handlebars, etc.). Cómo debe usar la propiedad el de la vista depende de lo que esté haciendo.

Elemento existente

El ejemplo de representación sugiere que tiene un elemento existente que está asignando a la vista, aunque no muestra la creación de instancias de las vistas. Si ese es el caso, y el elemento ya está en el documento, entonces puede hacer algo como esto (actualizar el contenido de el , pero no alterar el mismo):

render : function () { this.$el.html( "Some stuff" ); }

http://jsfiddle.net/vQMa2/1/

Elemento generado

Digamos que no tiene un elemento existente y permite que Backbone genere uno para usted. Es posible que desee hacer algo como esto (pero es probable que sea mejor diseñar cosas para que su vista no sea responsable de conocer nada fuera de sí misma):

render : function () { this.$el.html( "Some stuff" ); $( "#some-container" ).append( this.el ); }

http://jsfiddle.net/vQMa2/

Plantillas

En mi caso, estoy usando plantillas, por ejemplo:

<div class="player" id="{{id}}"> <input name="name" value="{{name}}" /> <input name="score" value="{{score}}" /> </div> <!-- .player -->

La plantilla representa la vista completa. En otras palabras, no habrá envoltorio alrededor de la plantilla: div.player será el elemento raíz o más externo de mi vista.

Mi clase de jugador se verá algo así (con un ejemplo muy simplificado de render() ):

Backbone.View.extend( { tagName : ''div'', className : ''player'', attributes : function () { return { id : "player-" + this.model.cid }; }, // attributes render : function { var rendered_template = $( ... ); // Note that since the top level element in my template (and therefore // in `rendered_template`) represents the same element as `this.el`, I''m // extracting the content of `rendered_template`''s top level element and // replacing the content of `this.el` with that. this.$el.empty().append( rendered_template.children() ); } } );


Debes eliminar tagName y declarar un el.

''tagName'' significa que desea que la red troncal cree un elemento. Si el elemento ya existe en el DOM, puede especificar un el como:

el: $(''#emotions''),

y después:

render: function() { $(this.el).append(this.model.toJSON()); }


En tu opinión, haz algo como esto

var ItemView = Backbone.View.extend({ tagName: "div", // I know it''s the default... render: function() { $(this.el).attr(''id'', ''id1'').addClass(''nice'').html(''Some Stuff''); } });


Esta es una forma mínima de cambiar dinámicamente la clase del elemento de la vista a través de un modelo y actualizarlo en los cambios del modelo.

var VMenuTabItem = Backbone.View.extend({ tagName: ''li'', events: { ''click'': ''onClick'' }, initialize: function(options) { // auto render on change of the class. // Useful if parent view changes this model (e.g. via a collection) this.listenTo(this.model, ''change:active'', this.render); }, render: function() { // toggle a class only if the attribute is set. this.$el.toggleClass(''active'', Boolean(this.model.get(''active''))); this.$el.toggleClass(''empty'', Boolean(this.model.get(''empty''))); return this; }, onClicked: function(e) { if (!this.model.get(''empty'')) { // optional: notify our parents of the click this.model.trigger(''tab:click'', this.model); // then update the model, which triggers a render. this.model.set({ active: true }); } } });


Intenta asignar los valores en el método de inicialización. Esto asignará directamente el id y la clase al atributo div de forma dinámica.

var ItemView = Backbone.View.extend( { tagName : "div", id : '''', class : '''', initialize : function( options ) { if ( ! _.isUndefined( options ) ) { this.id = options.item_id; this.class= options.item_class; } }, render : function() { $( this.el ).html( this.template( "stuff goes here" ) ); } } );


Los otros ejemplos no muestran cómo tomar realmente los datos del modelo. Para agregar dinámicamente id y class a partir de los datos del modelo:

var ItemView = Backbone.View.extend({ tagName: "div", render: function() { this.id = this.model.get(''item_id''); this.class = this.model.get(''item_class''); $(this.el).attr(''id'',this.id).addClass(this.class).html(''Some Stuff''); } });


Puede establecer las propiedades className e id en el elemento raíz: http://documentcloud.github.com/backbone/#View-extend

var ItemView = Backbone.View.extend({ tagName: "div", // I know it''s the default... className : ''nice'', id : ''id1'', render: function() { $(this.el).html("Some stuff"); } });

EDITAR Ejemplo incluido de configuración de id en función de los parámetros del constructor

Si las vistas se construyen como se menciona:

var item1 = new ItemModel({item_class: "nice", item_id: "id1"}); var item2 = new ItemModel({item_class: "sad", item_id: "id2"});

Entonces los valores podrían establecerse de esta manera:

// ... className: function(){ return this.options.item_class; }, id: function(){ return this.options.item_id; } // ...


Sé que es una vieja pregunta, pero que se agregó como referencia. Esto parece ser más fácil en las nuevas versiones de red troncal. En Backbone 1.1 las propiedades id y className se evalúan en la función ensureElement (ver de fuente ) usando underscore _.result significa que className o id es una función, se llamará, de lo contrario se usará su valor.

Entonces podrías dar className directamente en el constructor, dar otro parámetro que se usaría en className, etc ... Un montón de opciones

entonces esto debería funcionar

var item1 = new ItemModel({item_class: "nice", item_id: "id1"}); var item2 = new ItemModel({item_class: "sad", item_id: "id2"}); var ItemView = Backbone.View.extend({ id: function() { return this.model.get(''item_id''); }, className: function() { return this.model.get(''item_class''); } });