2013-03-25 8 views
9

Ho creato un esempio molto rapido di attivazione di avviso due volte su Vista dorsale.Il clic sta chiamando la funzione due volte sul backbone

http://jsfiddle.net/feronovak/8svqX/

Questo è non fa nulla di speciale, è davvero per me capire come chiamare correttamente separati Visualizzazioni, così essi non innescare precedente metodo click. In questo momento creo due viste, la seconda vista sovrascrive il modulo in #boxSearch. Quando faccio clic sul pulsante Cerca, viene generato anche l'avviso doSearch SearchLocation. Mi aspettavo di vedere solo l'avviso doSearch SearchLatitude. Cosa ho fatto di sbagliato?

var Geo = { 
    Views: {}, 
    Templates: {} 
}; 

Geo.Templates.SearchLocation = _.template($("#tpl-search-location").html()); 
Geo.Templates.SearchLatitude = _.template($("#tpl-search-latitude").html()); 

Geo.Views.SearchLocation = Backbone.View.extend({ 
    initialize: function() { 
     this.render(); 
    }, 
    el: $("#boxSearch"), 
    template: Geo.Templates.SearchLocation, 
    render: function() 
    { 
     $(this.el).html(this.template); 
    }, 
    events: { 
     "click input[type=button]": "doSearch" 
    }, 
    doSearch: function(e) { 
     e.preventDefault(); 
     alert('doSearch SearchLocation'); 
    } 
}); 

Geo.Views.SearchLatitude = Backbone.View.extend({ 
    initialize: function() { 
     this.render(); 
    }, 
    el: $("#boxSearch"), 
    template: Geo.Templates.SearchLatitude, 
    render: function() 
    { 
     $(this.el).html(this.template); 
    }, 
    events: { 
     "click input[type=button]": "doSearch" 
    }, 
    doSearch: function(e) { 
     e.preventDefault(); 
     alert('doSearch SearchLatitude'); 
    } 
}); 

$(document).ready(function(e) { 
    var searchLocation = new Geo.Views.SearchLocation(); 
    var searchLatitude = new Geo.Views.SearchLatitude(); 
}); 

risposta

18

questo è il problema "fantasma vista" spesso visto con le applicazioni di dorsale:

  • si allega in vista di un elemento DOM
  • si collega un'altra vista allo stesso elemento DOM
  • si attiva un evento sull'elemento DOM (o peggio, attivare un evento da qualche altra parte che entrambe le visualizzazioni ascoltano)
  • Entrambe le visualizzazioni attivano i gestori degli eventi, spesso causando il caos.

Come @machineghost sottolinea, non c'è niente nel codice per svincolare la prima vista, in modo da entrambe le viste sono attaccati al #boxSearch, e entrambe le viste risponderà all'evento click.Ci sono diversi modi per aggirare questo:

  • Si può chiamare e.stopPropagation() (docs) nei gestori di eventi (credo che questo è quello che stai cercando di fare con e.preventDefault()). Ciò impedirà all'evento di ribollire quando viene attivato, quindi l'ultima vista definita lo catturerebbe. Funziona, ma nota che hai ancora le due viste in giro, e la vista fantasma obsoleta potrebbe avere altri effetti collaterali.

  • Si potrebbe avere ciascuna vista rendere il proprio elemento DOM, con una classe .boxSearch, e quindi chiamare view.remove() su quello che non si desidera più. Questa è probabilmente l'opzione più pulita, ma significa creare la maggior parte dei DOM al volo, che è un po 'più costoso e difficile da gestire rispetto alla codifica in HTML.

  • È possibile assegnare ad ogni vista un metodo di pulizia, ad es. .clear() o .exit(), che potrebbe chiamare view.undelegateEvents() e view.stopListening(). Quindi assicurati di chiamare questo metodo quando hai finito con una vista.

Se stai attento a fare in modo che venga chiamato e assicurandosi che cancelli tutto ciò che deve essere cancellato, allora funziona bene.

+0

Questa è un'ottima risposta. Ha davvero buoni dettagli e spiega perché è un problema e come risolverlo. Grazie mille. – Termato

2

Quando aggiungi un'altra vista a un elemento, questa non rimuove automaticamente quella precedente; se vuoi farlo devi chiamare esplicitamente lo yourFirstView.remove(). Tuttavia, fare ciò rimuoverà anche l'elemento; se vuoi semplicemente separare gli eventi senza rimuovere l'elemento, puoi usare invece yourFirstView.undelegateEvents().

Vale la pena ricordare che si tratta di un comportamento deliberato di Backbone: spesso è utile avere 2 o più viste su un singolo el.

+0

Nota: non utilizzare 'view.remove()' qui - questo rimuoverà l'elemento '# boxSearch' dal DOM, non solo per separare la vista. – nrabinowitz

+0

Oh whoops, ho dimenticato Backbone; Modificherò la mia risposta. – machineghost

0

Quando dorsale utilizza jQuery (o Zepto ... a seconda di quale, lascia solo supporre jQuery e ti permettono di sostituire, se necessario) per associare eventi ai elementi all'interno della vista, gestisce gli eventi come se l'evento gorgogliare su dal elemento all'elemento el.

È possibile dove questo comportamento proviene da delegateEvents nella sorgente annotato:

http://documentcloud.github.com/backbone/docs/backbone.html

delegateEvents: function(events) { 
    if (!(events || (events = _.result(this, 'events')))) return this; 
    this.undelegateEvents(); 
    for (var key in events) { 
    var method = events[key]; 
    if (!_.isFunction(method)) method = this[events[key]]; 
    if (!method) continue; 

    var match = key.match(delegateEventSplitter); 
    var eventName = match[1], selector = match[2]; 
    method = _.bind(method, this); 
    eventName += '.delegateEvents' + this.cid; 
    if (selector === '') { 
     this.$el.on(eventName, method); 
    } else { 
     this.$el.on(eventName, selector, method); 
    } 
    } 
    return this; 
}, 

La chiave è capire gli eventi "delegare-grado" rispetto alla this.$el.on(eventName, method); e this.$el.on(eventName, selector, method);

Per jQuery, vedere http://api.jquery.com/on/, sezione "Eventi diretti e delegati".

Per quanto riguarda il codice, si spera che si possa capire perché si sta comportando così com'è. Non si sovrascrive l'elemento elstesso, quindi entrambi i collegamenti delegati sono ancora validi.

0

A volte si desidera assicurarsi che la gerarchia della vista non si traduca in sovrapposizione di listener di ascoltatori. Ad esempio, se si dispone di una vista ad albero con UL-s con sub UL-s. E questi UL esistono nel DOM e vuoi associare le tue viste della dorsale a qualche pulsante all'interno di ;; LIs. allora si dovrebbe usare più rigorosa di selezione con l'aiuto di ">", cioè invece di:

"click li .btn": "handleClick" 

si avrebbe:

"click li > .btn": "handleClick"