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!
// 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
etclassName
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é.
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' ) );
};
el
, rendre, éviter le wrapperDans 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.
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" );
}
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 );
}
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() );
}
} );
À 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');
}
});
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;
}
// ...
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'); }
});
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');
}
});
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());
}
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" ) );
}
} );
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 });
}
}
});