Impostazione di id e className dinamicamente nelle viste Backbone.js


85

Sto imparando e utilizzando Backbone.js.

Ho un modello articolo e una vista articolo corrispondente. Ogni istanza del modello ha gli attributi item_class e item_id, in cui desidero che si riflettano come attributi "id" e "class" della vista corrispondente. Qual è il modo corretto per ottenere questo risultato?

Esempio:

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

Come dovrei implementare la vista in modo che gli el delle viste si traducano in:

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

Nella maggior parte degli esempi che ho visto, l' el della vista funge da elemento wrapper privo di significato all'interno del quale si deve scrivere manualmente il codice "semantico".

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

Quindi, quando reso, si ottiene

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

Ma questo sembra uno spreco: perché il div esterno? Voglio che el si traduca direttamente nel div interno!

Risposte:


133

Riepilogo: imposta dinamicamente gli attributi della vista con i dati del modello

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"
  } )
} );
  • Questo esempio presuppone che tu stia consentendo a Backbone di generare un elemento DOM per te.

  • Il attributesmetodo viene chiamato dopo che le proprietà passate al costruttore della vista sono state impostate (in questo caso, model), consentendo di impostare dinamicamente gli attributi con i dati del modello prima della creazione di Backbone el.

  • In contrasto con alcune delle altre risposte: non codifica i valori degli attributi nella classe di visualizzazione, li imposta dinamicamente dai dati del modello; non aspetta render()di impostare attr vals; non imposta ripetutamente attr vals in ogni chiamata a render(); non imposta manualmente inutilmente i valori attr sull'elemento DOM.

  • Si noti che se l'impostazione della classe quando si chiama Backbone.View.extendo di un costruttore vista (ad esempio new Backbone.View), è necessario utilizzare il nome della proprietà DOM, classNamema se l'impostazione tramite il attributeshash / metodo (come in questo esempio) è necessario utilizzare il nome dell'attributo, class.

  • A partire da Backbone 0.9.9:

    Quando si dichiara una vista ... el, tagName, ide classNamepuò ora essere definito come funzioni, se si vuole i loro valori da determinare in fase di esecuzione.

    Lo menziono nel caso in cui ci sia una situazione in cui sarebbe utile come alternativa all'utilizzo di un attributesmetodo come illustrato.

Utilizzando un elemento esistente

Se stai usando un elemento esistente (es. Passando elal costruttore della vista) ...

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

... allora attributesnon verrà applicato all'elemento. Se gli attributi desiderati non sono già impostati sull'elemento, o non si vuole duplicare che i dati nella classe di visualizzazione e un altro percorso, quindi si consiglia di aggiungere un initializemetodo per la visualizzazione costruttore che si applica attributesa el. Qualcosa di simile (usando jQuery.attr):

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

Utilizzo di el, rendering, evitando il wrapper

Nella maggior parte degli esempi che ho visto, el della vista serve come un elemento wrapper senza significato all'interno del quale si deve scrivere manualmente il codice "semantico".

Non c'è motivo view.elper cui debba essere "un elemento wrapper privo di significato". In effetti, ciò spesso romperebbe la struttura DOM. Se una classe di visualizzazione rappresenta un <li>elemento, ad esempio, deve essere renderizzato come un <li>- renderlo come un <div>o qualsiasi altro elemento interromperà il modello di contenuto. È probabile che vuole mettere a fuoco su come impostare correttamente elemento del vostro vista (utilizzando le proprietà come tagName, className, e id) e poi di pronunciare la contenuti da allora in poi.

Le opzioni su come far interagire gli oggetti della vista Backbone con il DOM sono completamente aperte. Esistono 2 scenari iniziali di base:

  • Puoi collegare un elemento DOM esistente a una vista Backbone.

  • È possibile consentire a Backbone di creare un nuovo elemento disconnesso dal documento, quindi inserirlo in qualche modo nel documento.

Esistono vari modi per generare il contenuto per l'elemento (impostare una stringa letterale, come nel tuo esempio; utilizzare una libreria di modelli come Moustache, Handlebars, ecc.). Il modo in cui dovresti usare la elproprietà della vista dipende da cosa stai facendo.

Elemento esistente

Il tuo esempio di rendering suggerisce che hai un elemento esistente che stai assegnando alla vista, sebbene non mostri l'istanza delle viste. Se è così e l'elemento è già nel documento, allora potresti voler fare qualcosa di simile (aggiorna il contenuto di el, ma non alterare else stesso):

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

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

Elemento generato

Diciamo che non hai un elemento esistente e permetti a Backbone di generarne uno per te. Si potrebbe voler fare qualcosa di simile (ma è probabile meglio le cose architetto in modo che la vista non è responsabile di conoscere qualsiasi cosa fuori di sé):

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

http://jsfiddle.net/vQMa2/

Modelli

Nel mio caso, sto usando modelli, ad esempio:

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

Il modello rappresenta la vista completa. In altre parole, non ci sarà alcun wrapper attorno al modello: div.playersarà l'elemento principale o più esterno della mia vista.

La mia classe giocatore sarà simile a questa (con un esempio molto semplificato di 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() );
  }      
} );

Un modo fantastico per sovrascrivere la proprietà degli attributi con una funzione e restituire nuovamente un oggetto!
Kel

2
@Kel sì, è un buon modo per realizzare qualcosa di dinamico come ciò che la domanda chiede, popolando gli attributi con i dati del modello, senza dover utilizzare codice ripetitivo in cui vengono istanziate le viste. Probabilmente lo sai, ma nel caso in cui non sia ovvio, è una caratteristica di Backbone di cui puoi usare una funzione che restituisce un hash come valore attributes, come un numero di altre proprietà di Backbone che possono essere fornite come una funzione o qualche altra tipo di valore. In questi casi Backbone controlla se il valore è una funzione, la chiama e utilizza il valore restituito.
JMM

95

Dal tuo punto di vista fai qualcosa del genere

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

Questa è la risposta corretta a questa domanda e dovrebbe essere accettata
reach4thelasers

13
Questa risposta non dimostra l'impostazione dinamica degli attributi di visualizzazione in base ai dati del modello, mostra solo un metodo alternativo di hard-coding dei valori degli attributi.
JMM

3
@JMM - Neanche il suo codice di esempio utilizza i dati del modello. Questa risposta funziona in base al suo codice di esempio. Ovviamente i dati del modello potrebbero essere sostituiti ai valori.
Clint

5
@ Clint, non conterei che sia ovvio per l'OP. "Anche il suo codice di esempio non utilizza i dati del modello." - questo perché non sa come, e quindi perché ha chiesto aiuto. Mi sembra chiaro che stia chiedendo come impostare gli attributi di view.el utilizzando i dati del modello e non ha idea di come procedere. La risposta non mostra nemmeno come farlo, e perché dovresti aspettare fino al rendering per farlo comunque, o farlo di nuovo ogni volta che esegui il rendering? "Questa risposta funziona ..." - come funziona? Ogni vista creata in questo modo avrebbe gli stessi attributi. L'unica cosa che mostra è come evitare il wrapper.
JMM

L'OP è scomparso dal febbraio '12. :( Ecco un altro +1 per questa risposta.
Almo

27

Puoi impostare le proprietà classNamee idsull'elemento root: 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");
   }       
});

MODIFICA Incluso esempio di impostazione dell'ID in base ai parametri del costruttore

Se le viste sono costruite come indicato:

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

Quindi i valori potrebbero essere impostati in questo modo:

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

3
Non credo che questa risposta sia corretta perché poi tutti ItemViewavranno id: 'id1'. Questo deve essere calcolato in base al tempo di esecuzione in model.id.
fguillen

Ovviamente puoi impostare l'ID come preferisci. Usa una funzione, una variabile o qualsiasi altra cosa. Il mio codice include solo un esempio, indicando come impostare il valore sull'elemento radice.
Jørgen

Ho aggiunto un esempio che chiarisce come impostare i valori dinamicamente in base ai parametri del costruttore.
Jørgen

Questa è la risposta corretta. Utilizza correttamente le funzionalità Backbone per risolvere il problema.
Marc-Antoine Lemieux

6

So che è una vecchia domanda, ma aggiunta come riferimento. Questo sembra essere più facile nelle nuove versioni di backbone. In Backbone 1.1 le proprietà id e className vengono valutate nella funzione ensureElement(vedi dal sorgente ) usando il _.resultsignificato di sottolineatura se classNameo idè una funzione, verrà chiamata, altrimenti verrà usato il suo valore.

Quindi potresti dare className direttamente nel costruttore, dare un altro parametro che verrebbe usato in className, ecc ... Molte opzioni

quindi dovrebbe funzionare

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

Il tuo esempio non è valido, vorrestiid: function() { return this.model.get('item_id'); })
Cobby

4

Gli altri esempi non mostrano come acquisire effettivamente i dati dal modello. Per aggiungere dinamicamente ID e classe dai dati del modello:

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

È "this.className" o "this.class"?
Gabe Rainbow

2

Devi rimuovere tagName e dichiarare un el.

'tagName' significa che vuoi che backbone crei un elemento. Se l'elemento esiste già nel DOM, puoi specificare un el come:

el: $('#emotions'),

e più tardi:

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

2

Prova ad assegnare i valori nel metodo initialize questo assegnerà dinamicamente id e class all'attributo div direttamente.

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

@Michel Pleasae passa attraverso questa documentazione, backbonejs.org/#View-constructor
Hemanth

0

Ecco un modo minimo per modificare dinamicamente la classe dell'elemento della vista tramite un modello e aggiornarlo alle modifiche del modello.

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 });
        }
    }
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.