Establecer id y className dinámicamente en vistas de Backbone.js

85

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 atributos item_class y item_id, en los que quiero que se refleje como los atributos 'id' y 'class' de la vista correspondiente. ¿Cuál es la forma correcta de lograrlo?

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 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, de la vista el sirve como un elemento dentro de la envoltura sin sentido, que uno tiene que escribir manualmente el código 'semántica'.

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 renderiza, 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 se traduzca directamente en el div interno!

Nadav
fuente

Respuestas:

133

Resumen: establecer atributos de vista de forma dinámica con datos del 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 asume que está permitiendo que Backbone genere un elemento DOM por usted.

  • El attributesmétodo se llama después de que se establecen las propiedades pasadas al constructor de la 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()establecer attr vals; no establece repetidamente attr vals en cada llamada a render(); no establece innecesariamente manualmente los valores de atributo en el elemento DOM.

  • Tenga en cuenta que si el establecimiento de la clase cuando se llama Backbone.View.extendo una vista de constructor (por ejemplo new Backbone.View), usted tiene que utilizar el nombre de la propiedad DOM, classNamepero si su creación a través de la attributesalmohadilla / método (como en este ejemplo) tiene que usar el nombre del atributo, class.

  • A partir de Backbone 0.9.9:

    Cuando se declara una vista ... el, tagName, idy classNameahora puede definirse como funciones, si quieres sus valores que deben determinarse en tiempo de ejecución.

    Menciono esto en caso de que exista una situación en la que sea útil como alternativa al uso de un attributesmétodo como se ilustra.

Usando un elemento existente

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

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

... entonces attributesno se aplicará 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 initializemétodo a su constructor de vista que se aplique attributesa 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 el de la vista sirve como un elemento contenedor sin sentido dentro del cual uno tiene que escribir manualmente el código 'semántico'.

No hay ninguna razón view.elpara que sea "un elemento contenedor sin sentido". De hecho, eso a menudo rompería la estructura DOM. Si una clase de vista representa un <li>elemento, por ejemplo, necesita ser renderizado como un <li>- renderizarlo como un <div>o cualquier otro elemento rompería el modelo de contenido. Es probable que desea enfocar correctamente la configuración de elemento de la vista (utilizando propiedades como tagName, classNamey id) y luego emitir su contenido a partir de entonces.

Las opciones sobre cómo hacer que los objetos de la vista Backbone interactúen con el DOM están abiertas. 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 formas de generar el contenido del elemento (establezca una cadena literal, como en su ejemplo; use una biblioteca de plantillas como Moustache, Handlebars, etc.). La forma en que debe usar la elpropiedad de la vista depende de lo que esté haciendo.

Elemento existente

Su ejemplo de representación sugiere que tiene un elemento existente que está asignando a la vista, aunque no muestra la instanciación de las vistas. Si ese es el caso, y el elemento ya está en el documento, entonces es posible que desee hacer algo como esto (actualice el contenido de el, pero no se altere el):

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 por usted. Es posible que desee hacer algo como esto (pero probablemente sea mejor diseñar las cosas para que su vista no sea responsable de saber nada fuera de sí mismo):

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á ningún envoltorio alrededor de la plantilla:div.player será la raíz o el elemento más externo de mi vista.

Mi clase de jugador se verá 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() );
  }      
} );
JMM
fuente
¡Una manera increíble de sobrescribir la propiedad de atributos con una función y devolver un objeto nuevamente!
Kel
2
@Kel, sí, es una buena manera de lograr algo dinámico como lo que pide la pregunta, rellenando los atributos con datos del modelo, sin tener que usar código repetitivo donde se crean instancias de las vistas. Probablemente sepa esto, pero en caso de que no sea obvio, es una característica de Backbone que puede usar una función que devuelve un hash como valor attributes, como una serie de otras propiedades de Backbone que se pueden suministrar como una función o alguna otra. tipo de valor. En esos casos, Backbone comprueba si el valor es una función, lo llama y usa el valor de retorno.
JMM
95

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'); 
   }       
});
Dan Brooke
fuente
Esta es la respuesta correcta a esta pregunta y debe aceptarse
reach4thelasers
13
Esta respuesta no demuestra la configuración dinámica de los atributos de vista según los datos del modelo, solo muestra un método alternativo para codificar los valores de los atributos.
JMM
3
@JMM: su código de muestra tampoco está usando los datos del modelo. Esta respuesta funciona según su código de muestra. Obviamente, los datos del modelo podrían sustituirse por los valores.
Clint
5
@Clint, no contaría con que eso sea obvio para el OP. "Su código de muestra tampoco está usando los datos del modelo". - Eso es porque no sabe cómo, y por eso pidió ayuda. Me parece claro que está preguntando cómo establecer atributos de view.el usando datos del modelo y no tiene idea de cómo hacerlo. La respuesta ni siquiera muestra cómo hacer eso, y ¿por qué esperarías hasta renderizar para hacerlo de todos modos, o volver a hacerlo cada vez que renderizas? "Esta respuesta funciona ..." - ¿cómo funciona? Cada vista creada de esa manera tendría los mismos atributos. Lo único que muestra es cómo evitar la envoltura.
JMM
El OP se ha ido desde febrero del '12. :( Aquí hay otro +1 para esta respuesta.
Almo
27

Puede configurar las propiedades classNamey iden 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 basado en parámetros de 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;
}
// ...
Jørgen
fuente
3
No siento que esta respuesta sea correcta porque entonces todos lo ItemViewhabrán hecho id: 'id1'. Esto debe calcularse en tiempo de ejecución basado en el model.id.
fguillen
Por supuesto, puede configurar el ID de la forma que desee. Utilice una función, variable o cualquier cosa. Mi código solo incluye un ejemplo, que indica cómo establecer el valor en el elemento raíz.
Jørgen
Agregué un ejemplo que aclara cómo establecer los valores de forma dinámica en función de los parámetros del constructor.
Jørgen
Esta es la respuesta correcta. Utiliza las funciones de Backbone correctamente para resolver el problema.
Marc-Antoine Lemieux
6

Sé que es una pregunta antigua, pero agregada como referencia. Esto parece ser más fácil en las nuevas versiones de backbone. En Backbone 1.1, las propiedades id y className se evalúan en la función ensureElement(ver de la fuente ) usando un subrayado que _.resultsignifica que si classNameo ides una función, se llamará, de lo contrario, se usará su valor.

Entonces podría 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'); }
});
Marcus
fuente
Tu ejemplo no es válido, querríasid: function() { return this.model.get('item_id'); })
Cobby
4

Los otros ejemplos no muestran cómo tomar realmente los datos del modelo. Para agregar dinámicamente id y clase 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'); 
   }       
});
diskodave
fuente
¿Eso es 'this.className' o 'this.class'?
Gabe Rainbow
2

Necesita 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());
}
jskulski
fuente
2

Intente asignar los valores en el método initialize, esto asignará directamente id y class 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" ) ); 
    }
} );
Hemanth
fuente
@Michel Pleasae revisa esta documentación, backbonejs.org/#View-constructor
Hemanth
0

Aquí hay una forma mínima de cambiar la clase del elemento de la vista dinámicamente 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 });
        }
    }
});
Emile Bergeron
fuente