2011-12-22 1 views
17

Sappiamo tutti fare qualcosa di simile è male:Delega di eventi a sub-viste in Backbone.js

<ul> 
    <li>Item</li> 
    <li>Item</li> 
    ... 500 more list items 
</ul> 

e poi ...

$("ul li").bind("click", function() { ... }); 

Ho cercato attraverso un sacco di esempi/guide di Backbone e il seguente sembra essere un approccio standard per il rendering di un elenco con elementi, basato su una raccolta di modelli.

var ListView = Backbone.View.extend() { 

    tagName: 'ul', 

    render: function() { 
    this.collection.each(function(item) { 
     var view = new ListItemView({model: item}); 
     $(this.el).append(view.render().el); 
    }); 
    return this; 
    } 
}); 

Una vista elemento della lista:

var ListItemView = Backbone.View.extend() { 

    tagName: 'li', 

    events: { 
    'click' : 'log' 
    } 

    log : function() { 
    console.log(this.model.get("title")); 
    } 

    render: function() { 
    $(this.el).html(this.template(this.model.toJSON())); 
    return this; 
    } 
}); 

Se non mi sbaglio, istanziare la listView con una collezione di 500 modelli, mi dà 500 eventi click, uno per ogni riga. Questo è male vero?

So Backbone ha costruito in delegazione evento per gli eventi namespace:

events : { 
    'click li' : 'log' 
} 

Suppongo che potrei mettere questo nel mio ListView, e sarebbe creare un solo evento click per l'intera lista, ma poi ho wouldn essere in grado di accedere ai dati del modello corrispondenti alla voce dell'elenco cliccato.

Quali sono i pattern utilizzati dagli sviluppatori di backbone per risolvere questo tipico problema?

risposta

2

È possibile associare l'istanza con un elemento in questo modo:

events : { 
    'click li' : 'log' 
}, 

log: function(e) { 
var elm = e.currentTarget //Same as `this` in normally bound jQuery event 


jQuery.data(elm, "viewInstance").log(e); 
}, 

Poi:

var ListItemView = Backbone.View.extend() { 

    tagName: 'li', 

    log : function() { 
    console.log(this.model.get("title"); 
    } 

    render: function() { 
     //Associate the element with the instance 
    $(this.el).html(this.template(this.model.toJSON())).data("viewInstance", this); 
    return this; 
    } 
}); 
+1

Grazie per la risposta. Questo è un po 'quello che ho dovuto fare per aggirare questo problema fino ad ora, ma poi ancora, Sta usando jQuery per memorizzare i dati nel DOM, non sembra un approccio molto pulito a lungo termine. – Daniel

+1

@Daniel Beh jQuery non memorizza i dati nel DOM, lo memorizza in un oggetto js regolare ('jQuery.cache'). jQuery interviene anche internamente su qualsiasi elemento che abbia uno o più eventi con una proprietà expando, dopodiché fare 'data' sull'elemento non ha alcun effetto se non l'aggiunta di ulteriori proprietà nell'oggetto js regolare. – Esailija

+0

Ah Ok. beh, immagino che sarebbe una specie di lavoro allora. grazie – Daniel

3

Tieni traccia delle sottoview dalla vista principale. Quindi, quando aggiungi la sottoview, aggiungilo all'hash e aggiungi il cid all'el della sottoview. In questo modo hai un puntatore alla sottoview e potresti eseguire operazioni sul suo modello ecc ...

Non ho testato questo codice esatto qui sotto, quindi questo può essere sbagliato in un posto o due, ma ho testato questo principio generale. Ho anche omesso il codice listitemview.

var ListView = Backbone.View.extend() { 
    subViews: {}, 
    tagName: 'ul', 
    events: { 
    'click li' : 'clickItem' 
    }, 
    clickItem: function(event){ 
    var id = event.currentTarget.cid; 
    var subView = this.subViews[id]; 


    }, 
    render: function() { 

    this.collection.each(function(item) { 
     var view = new ListItemView({model: item}); 
     this.subViews[view.cid] = view; 
     subEl = view.render().el; 
     subEl.cid = view.cid; 
     $(this.el).append(subEl); 
    }); 
    return this; 
    } 
});