web-dev-qa-db-fra.com

Définition dynamique de id et className dans les vues Backbone.js

Je suis en train d'apprendre et d'utiliser Backbone.js.

J'ai un modèle Item et une vue Item correspondante . Chaque instance de modèle a les attributs item_class et item_id, que je souhaite voir reflétés dans les attributs 'id' et 'class' de la vue correspondante ..__ bonne façon d'y parvenir? 

Exemple:

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

Comment dois-je implémenter la vue pour que les éléments de la vue se traduisent par:

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

Dans la plupart des exemples que j'ai vus, la vue el sert d'élément de wrapper sans signification à l'intérieur duquel il faut écrire manuellement le code "sémantique".

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

Donc quand on le rend, on obtient 

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

Mais cela semble être un gaspillage - pourquoi avoir la division externe? je veux le el traduire directement dans le div interne!

84
Nadav

Résumé: définition dynamique des attributs de vue avec les données de modèle

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"
  } )
} );
  • Cet exemple suppose que vous autorisez Backbone à générer un élément DOM pour vous.

  • La méthode attributes est appelée une fois les propriétés transmises au constructeur de vue définies (dans ce cas, model), ce qui vous permet de définir de manière dynamique les attributs avec les données de modèle avant que Backbone ne crée el.

  • Contrairement à certaines des autres réponses: ne codent pas en dur les valeurs d'attribut dans la classe de vue, les définit de manière dynamique à partir des données de modèle; n'attend pas le render() pour définir attr vals; ne définit pas à plusieurs reprises attr vals dans chaque appel sur render(); ne définit pas inutilement manuellement des valeurs attr sur un élément DOM.

  • Notez que si vous définissez la classe lorsque vous appelez Backbone.View.extend ou un constructeur de vue (par exemple, new Backbone.View), vous devez utiliser le nom de la propriété DOM, className, mais si vous le définissez via la variable attributes hash/method (comme dans cet exemple), vous devez utiliser nom d'attribut, class.

  • À partir de l'épine dorsale 0.9.9:

    Lors de la déclaration d'une vue ...el, tagName, id et className peuvent maintenant être définies en tant que fonctions, si vous souhaitez que leurs valeurs soient déterminées au moment de l'exécution.

    Je le mentionne au cas où cela serait utile comme alternative à l’utilisation d’une méthode attributes comme illustré.

Utiliser un élément existant

Si vous utilisez un élément existant (par exemple, passez el au constructeur de la vue) ...

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

... alors attributes ne sera pas appliqué à l'élément. Si les attributs souhaités ne sont pas déjà définis sur l'élément ou si vous ne souhaitez pas dupliquer ces données dans votre classe de vue et un autre emplacement, vous pouvez alors ajouter une méthode initialize à votre constructeur de vue qui applique attributes à el. Quelque chose comme ça (en utilisant jQuery.attr):

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

Utilisation de el, rendre, éviter le wrapper

Dans la plupart des exemples que j'ai vus, el de la vue sert d'élément d'habillage sans signification à l'intérieur duquel il faut écrire manuellement le code "sémantique".

Il n'y a aucune raison pour que view.el doive être "un élément wrapper sans signification". En fait, cela briserait souvent la structure du DOM. Si, par exemple, une classe de vue représente un élément <li>, elle doit être restituée sous la forme d'un <li>; son rendu sous la forme d'un <div> sinon tout autre élément romprait le modèle de contenu. Vous voudrez probablement vous concentrer sur la configuration correcte de l'élément de votre vue (à l'aide de propriétés telles que tagName, className et id), puis le rendu de son contenu par la suite.

Les options permettant à vos objets de vue Backbone d’interagir avec le DOM sont grandes ouvertes. Il existe 2 scénarios initiaux de base:

  • Vous pouvez attacher un élément DOM existant à une vue Backbone.

  • Vous pouvez autoriser Backbone à créer un nouvel élément déconnecté du document, puis à l'insérer dans le document.

Il existe différentes manières de générer le contenu de l'élément (définissez une chaîne littérale, comme dans votre exemple; utilisez une bibliothèque de modèles, telle que Moustache, Handlebars, etc.). La manière dont vous devriez utiliser la propriété el de la vue dépend de ce que vous faites.

Élément existant

Votre exemple de rendu suggère que vous affectez un élément existant à la vue, bien que vous ne montriez pas l'instanciation des vues. Si tel est le cas et si l'élément est déjà dans le document, vous voudrez peut-être faire quelque chose comme ceci (mettez à jour le contenu de el, mais ne modifiez pas el lui-même):

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

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

Élément généré

Disons que vous n'avez pas d'élément existant et que vous permettez à Backbone d'en générer un pour vous. Vous pouvez vouloir faire quelque chose comme ceci (mais il est probablement préférable d'architecter des choses pour que votre vue ne soit pas responsable de la connaissance de quoi que ce soit en dehors de lui-même):

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

http://jsfiddle.net/vQMa2/

Modèles

Dans mon cas, j'utilise des modèles, par exemple:

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

Le modèle représente la vue complète. En d'autres termes, il n'y aura pas de wrapper autour du modèle - div.player sera l'élément racine ou le plus externe de ma vue.

Ma classe de joueurs ressemblera à ceci (avec un exemple très simplifié 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() );
  }      
} );
132
JMM

À votre avis, faites quelque chose comme ça

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'); 
   }       
});
94
Dan Brooke

Vous pouvez définir les propriétés className et id sur l'élément racine: 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");
   }       
});

EDIT Exemple inclus de définition d'un id en fonction de paramètres de constructeur

Si les vues sont construites comme indiqué:

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

Ensuite, les valeurs pourraient être définies de cette façon:

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

Je sais que c'est une vieille question, mais ajoutée pour référence. Cela semble être plus facile dans les nouvelles versions du réseau fédérateur. Dans Backbone 1.1, les propriétés id et className sont évaluées dans la fonction ensureElement (voir de la source ) en utilisant le trait de soulignement _.result, ce qui signifie que className ou id est une fonction.

Donc, vous pouvez donner className directement dans le constructeur, donner un autre paramètre qui serait utilisé dans className, etc ... Beaucoup d'options

alors ça devrait marcher

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'); }
});
6
Marcus

Les autres exemples ne montrent pas comment récupérer réellement les données du modèle. Pour ajouter dynamiquement id et class à partir des données du modèle:

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'); 
   }       
});
4
diskodave

Vous devez supprimer tagName et déclarer un el.

'tagName' signifie que vous voulez que le réseau crée un élément. Si l'élément existe déjà dans le DOM, vous pouvez spécifier un el comme:

el: $('#emotions'),

et ensuite:

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

Essayez d'attribuer les valeurs dans la méthode initialize, cela affectera directement id et class à l'attribut div de manière dynamique.

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" ) ); 
    }
} );
1
Hemanth

Voici un moyen minimal de changer la classe de l'élément de la vue de manière dynamique via un modèle et de le mettre à jour en fonction des modifications du modèle. 

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 });
        }
    }
});
0
Emile Bergeron