ID und Klassenname in Backbone.js-Ansichten dynamisch festlegen

85

Ich bin dabei, Backbone.js zu lernen und zu verwenden.

Ich habe ein Artikelmodell und eine entsprechende Artikelansicht. Jede Modellinstanz verfügt über die Attribute item_class und item_id, die als Attribute 'id' und 'class' der entsprechenden Ansicht wiedergegeben werden sollen. Was ist der richtige Weg, um dies zu erreichen?

Beispiel:

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

Wie soll ich die Ansicht implementieren, damit die Ansichten der Ansichten übersetzt werden in:

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

In den meisten Beispielen, die ich gesehen habe, dient das el der Ansicht als bedeutungsloses Wrapper-Element, in das man den 'semantischen' Code manuell schreiben muss.

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

Also, wenn gerendert, bekommt man

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

Aber das scheint eine Verschwendung zu sein - warum hat der externe Div? Ich möchte, dass das el direkt in das interne div übersetzt wird!

Nadav
quelle

Antworten:

133

Zusammenfassung: Ansichtsattribute dynamisch mit Modelldaten festlegen

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"
  } )
} );
  • In diesem Beispiel wird davon ausgegangen, dass Sie Backbone erlauben, ein DOM-Element für Sie zu generieren.

  • Die attributesMethode wird aufgerufen, nachdem die an den Ansichtskonstruktor übergebenen Eigenschaften festgelegt wurden (in diesem Fall model). Auf diese Weise können Sie die Attribute mit den Modelldaten dynamisch festlegen, bevor Backbone erstellt wird el.

  • Im Gegensatz zu einigen anderen Antworten: Codieren Sie Attributwerte in der Ansichtsklasse nicht fest, sondern setzen Sie sie dynamisch aus Modelldaten. wartet nicht bis render()zum Einstellen der Werte; setzt nicht wiederholt attr vals bei jedem Aufruf von render(); setzt attr vals für das DOM-Element nicht unnötig manuell.

  • Beachten Sie, dass Sie beim Festlegen der Klasse beim Aufrufen Backbone.View.extendoder eines Ansichtskonstruktors (z. B. new Backbone.View) den Namen der DOM-Eigenschaft verwenden müssen.className . Wenn Sie ihn jedoch über den attributesHash / die Methode festlegen (wie in diesem Beispiel), müssen Sie den Attributnamen verwenden class.

  • Ab Backbone 0.9.9:

    Wenn Sie eine Ansicht zu erklären ... el, tagName,id und classNamejetzt können als Funktionen definiert werden, wenn Sie ihre Werte wollen zur Laufzeit bestimmt werden.

    Ich erwähne dies für den Fall, dass es eine Situation gibt, in der dies als Alternative zur Verwendung von nützlich wäre attributes Methode wie abgebildet .

Verwenden eines vorhandenen Elements

Wenn Sie ein vorhandenes Element verwenden (z. B. elan den Ansichtskonstruktor übergeben) ...

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

... wird dann attributesnicht auf das Element angewendet. Wenn die gewünschten Attribute nicht bereits auf dem Element festlegen, oder Sie wollen nicht , dass die Daten in der Ansichtsklasse und einen anderen Ort zu kopieren, dann können Sie eine hinzufügen initializeMethode zu Ihrer Ansicht Konstruktor, gilt attributesauf el. So etwas (mit jQuery.attr):

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

Verwendung el, Rendern, Vermeiden des Wrappers

In den meisten Beispielen, die ich gesehen habe, dient das el der Ansicht als bedeutungsloses Wrapper-Element, in das man den 'semantischen' Code manuell schreiben muss.

Es gibt keinen Grund view.el, "ein bedeutungsloses Wrapper-Element" zu sein. Tatsächlich würde dies oft die DOM-Struktur brechen. Wenn eine Ansichtsklasse <li>beispielsweise ein Element darstellt, muss sie als - gerendert werden, da das <li>Rendern als <div>oder ein anderes Element das Inhaltsmodell beschädigen würde. Sie werden wahrscheinlich Ihre Ansicht des Elements wollen richtig konzentrieren sich auf die Einrichtung (mit Eigenschaften wie tagName, classNameund id) und dann machen seine Inhalte danach.

Die Optionen für die Interaktion Ihrer Backbone-Ansichtsobjekte mit dem DOM sind weit offen. Es gibt zwei grundlegende Ausgangsszenarien:

  • Sie können ein vorhandenes DOM-Element an eine Backbone-Ansicht anhängen.

  • Sie können Backbone erlauben, ein neues Element zu erstellen, das vom Dokument getrennt ist, und es dann irgendwie in das Dokument einfügen.

Es gibt verschiedene Möglichkeiten, wie Sie den Inhalt für das Element generieren können (legen Sie wie in Ihrem Beispiel eine Literalzeichenfolge fest; verwenden Sie eine Vorlagenbibliothek wie Moustache, Lenker usw.). Wie sollten Sie die verwendenel Eigenschaft der Ansicht verwenden sollten, hängt davon ab, was Sie tun.

Vorhandenes Element

In Ihrem Rendering-Beispiel wird vorgeschlagen, dass Sie ein vorhandenes Element haben, das Sie der Ansicht zuweisen, obwohl Sie keine Instanziierung der Ansichten anzeigen. Wenn dies der Fall ist und sich das Element bereits im Dokument befindet, möchten Sie möglicherweise Folgendes tun (aktualisieren Sie den Inhalt von el, ändern Sie elsich jedoch nicht selbst):

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

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

Generiertes Element

Angenommen, Sie haben kein vorhandenes Element und lassen Backbone eines für Sie generieren. Sie können so etwas wie dies tun wollen (aber es ist wahrscheinlich besser Architekt Dinge , so dass Ihre Ansicht zu wissen , über alles , was außen nicht verantwortlich ist selbst):

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

http://jsfiddle.net/vQMa2/

Vorlagen

In meinem Fall verwende ich Vorlagen, z.

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

Die Vorlage repräsentiert die vollständige Ansicht. Mit anderen Worten, es wird keinen Wrapper um die Vorlage geben - es div.playerwird das Stamm- oder äußerste Element meiner Ansicht sein.

Meine Spielerklasse sieht ungefähr so ​​aus (mit einem sehr vereinfachten Beispiel 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
quelle
Tolle Möglichkeit, die Attributeigenschaft mit einer Funktion zu überschreiben und ein Objekt erneut zurückzugeben!
Kel
2
@Kel Ja, es ist eine gute Möglichkeit, etwas Dynamisches zu erreichen, wie es die Frage verlangt, indem die Attribute mit Modelldaten gefüllt werden, ohne dass sich wiederholender Code verwendet werden muss, in dem die Ansichten instanziiert werden. Sie wissen das wahrscheinlich, aber für den Fall, dass es nicht offensichtlich ist, ist es eine Funktion von Backbone, dass Sie eine Funktion verwenden können, die einen Hash als Wert von zurückgibt attributes, wie eine Reihe anderer Backbone-Eigenschaften, die als Funktion oder eine andere bereitgestellt werden können Art des Wertes. In diesen Fällen prüft Backbone, ob der Wert eine Funktion ist, ruft ihn auf und verwendet den Rückgabewert.
JMM
95

Machen Sie aus Ihrer Sicht einfach so etwas

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
quelle
Dies ist die richtige Antwort auf diese Frage und sie sollte akzeptiert werden
erreichen4thelasers
13
Diese Antwort zeigt nicht das dynamische Festlegen der Ansichtsattribute basierend auf Modelldaten, sondern nur eine alternative Methode zum Hardcodieren der Attributwerte.
JMM
3
@JMM - Sein Beispielcode verwendet auch nicht die Modelldaten. Diese Antwort basiert auf seinem Beispielcode. Offensichtlich könnten die Daten durch Modelldaten ersetzt werden.
Clint
5
@Clint, ich würde nicht damit rechnen, dass dies für das OP offensichtlich ist. "Sein Beispielcode verwendet auch nicht die Modelldaten." - Das liegt daran, dass er nicht weiß wie und deshalb um Hilfe gebeten hat. Mir scheint klar zu sein, dass er fragt, wie attrs of view.el mithilfe von Modelldaten festgelegt werden sollen, und keine Ahnung hat, wie er vorgehen soll . Die Antwort zeigt nicht einmal, wie das geht, und warum sollten Sie bis zum Rendern warten, um es trotzdem zu tun, oder es jedes Mal wiederholen, wenn Sie rendern? "Diese Antwort funktioniert ..." - wie funktioniert es? Jede so erstellte Ansicht hätte die gleichen Werte. Das einzige, was es zeigt, ist, wie man den Wrapper vermeidet.
JMM
Das OP ist seit Februar '12 weg. :( Hier ist eine weitere +1 für diese Antwort.
Almo
27

Sie können die Eigenschaften classNameund iddas Stammelement festlegen : 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");
   }       
});

BEARBEITEN Enthaltenes Beispiel für die Einstellung der ID basierend auf Konstruktorparametern

Wenn die Ansichten wie erwähnt aufgebaut sind:

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

Dann könnten die Werte folgendermaßen eingestellt werden:

// ...
className: function(){
    return this.options.item_class;
},
id: function(){
    return this.options.item_id;
}
// ...
Jørgen
quelle
3
Ich fühle diese Antwort nicht richtig, weil dann jeder ItemViewhaben wird id: 'id1'. Dies muss in der Ausführungszeit basierend auf dem berechnet werden model.id.
Fguillen
Sie können die ID natürlich beliebig einstellen. Verwenden Sie eine Funktion, Variable oder etwas anderes. Mein Code enthält nur ein Beispiel, das zeigt, wie der Wert für das Stammelement festgelegt wird.
Jørgen
Ich habe ein Beispiel hinzugefügt, das verdeutlicht, wie die Werte basierend auf Konstruktorparametern dynamisch festgelegt werden.
Jørgen
Dies ist die richtige Antwort. Es verwendet Backbone-Funktionen korrekt, um das Problem zu lösen.
Marc-Antoine Lemieux
6

Ich weiß, dass es eine alte Frage ist, aber als Referenz hinzugefügt. Dies scheint in neuen Backbone-Versionen einfacher zu sein. Im Backbone 1.1 die ID und Klassennamen Eigenschaften sind in der Funktion ausgewertet ensureElement(siehe von der Quelle ) mit Strich _.resultbedeutet , wenn classNameoder ideine Funktion ist, wird sie aufgerufen werden, sonst wird ihr Wert verwendet werden.

Sie können also className direkt im Konstruktor angeben, einen anderen Parameter angeben, der im className verwendet wird, usw. Viele Optionen

Das sollte also funktionieren

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
quelle
Ihr Beispiel ist nicht gültig, Sie möchtenid: function() { return this.model.get('item_id'); })
Cobby
4

Die anderen Beispiele zeigen nicht, wie die Daten tatsächlich aus dem Modell abgerufen werden. So fügen Sie ID und Klasse dynamisch aus den Modelldaten hinzu:

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
quelle
Ist das 'this.className' oder 'this.class'?
Gabe Rainbow
2

Sie müssen tagName entfernen und ein el deklarieren.

'tagName' bedeutet, dass das Backbone ein Element erstellen soll. Wenn das Element bereits im DOM vorhanden ist, können Sie ein el wie folgt angeben:

el: $('#emotions'),

und später:

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

Versuchen Sie, die Werte in der Initialisierungsmethode zuzuweisen. Dadurch werden dem div-Attribut direkt ID und Klasse dynamisch zugewiesen.

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
quelle
@ Michael Pleasae gehen durch diese Dokumentation, backbonejs.org/#View-constructor
Hemanth
0

Hier ist eine minimale Möglichkeit, die Klasse des Ansichtselements dynamisch über ein Modell zu ändern und es bei Modelländerungen zu aktualisieren.

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
quelle