2011-10-04 14 views
7

Sto girando in tondo, apparentemente mi manca qualcosa nella mia app attuale implementando backbone.js. Il problema è che ho un appView master, che inizializza varie sottoview (un grafico, una tabella di informazioni, ecc.) Per la pagina. Il mio desiderio è di essere in grado di cambiare il layout della pagina a seconda di un flag di parametro passato durante la navigazione.Backbone View Nesting

Ciò che si verifica è un caso in cui le sottoview, che hanno riferimento agli elementi dom che sarebbero stati posizionati dopo il rendering del modello, non hanno accesso a tali elementi durante il processo di inizializzazione di AppView principale. Quindi la domanda principale è come faccio a garantire che gli elementi dom corretti siano presenti per ogni processo di bind di eventi per ottenere l'installazione corretta?

Con il seguente codice, se un evento è associato a un cambio di modello all'interno di LayoutView, il layout viene visualizzato ma le viste successive non vengono visualizzate correttamente. Una cosa che ho manipolato è stata quella di impostare tutti i valori '.el' delle viste su un elemento statico all'interno della struttura html. Questo fa sì che il rendering si verifichi, anche se sembra che si stacchi dalla buona capacità dell'ambito fornita dall'utilizzo di '.el'.

codice di esempio:

//Wrapped in jquery.ready() function... 
//View Code 
GraphView = Backbone.View.extend({ // init, model bind, template, supporting functions}); 

TableView = Backbone.View.extend({ // init, model bind, template, supporting functions}); 

LayoutView = Backbone.view.extend({ 
    initialize: function() { 
    this.model.bind('change:version', this.render, this); 
    }, 
    templateA = _.template($('#main-template').html()), 
    templateB = _.template($('#secondary-template').html()), 
    render: function() { 
    if ('main' == this.model.get('version')) { 
     this.el.html(this.templateA()); 
    } else { 
     this.el.html(this.templateB()); 
    } 
    }, 
}); 

MainView = Backbone.View.extend({ 
    initialize: function() { 
    this.layoutView = new LayoutView({ 
     el: $('#layoutContainer'), 
     model: layout, 
    }); 
    this.graphView = new GraphView({ 
     el: $('.graphContainer'), 
     model: graph 
    }); 
    this.tableView = new TableView({ 
     el: $('#dataTableContainer', 
     model: dataSet 
    }); 
    }, 
}); 

// Model Code 
Layout = Backbone.Model.extend({ 
    defaults: { 
    version: 'main', 
    }, 
}); 

Graph = Backbone.Model.extend({}); 
dataSet = Backbone.Model.extend({}); 

// Router code... 

// Fire off the app 
AppView = new MainView($('#appContainer')); 
// Create route, start up backbone history 

HTML: Per semplicità Ho appena risistemato div tra i due layout.

<html> 
    <head> 
    <!-- include above js and backbone dependancies --> 
    </head> 
    <body> 
    <script type="text/template" id="main-template"> 
     <div class="graphContainer"></div> 
     <div class="dataTableContainer"></div> 
     <div>Some other stuff to identify that it's the main template</div> 
    </script> 
    <script type="text/template" id="secondary-template"> 
     <div class="dataTableContainer"></div> 
     <div>Rock on layout version two!</div> 
     <div class="graphContainer"></div> 
    </script> 
    <div id="appContainer"> 
     <div id="nav"> 
     <a href="#layout/main">See Layout One</a> 
     <a href="#layout/secondary">See Layout Two</a> 
     </div> 
     <div id="layoutContainer"></div> 
    </div> 
    </body> 
</html> 

Apprezzo l'intuizione/input che qualcuno di voi può avere. Grazie!

+0

Solo FYI, hai un sacco di errori di battitura nel tuo codice - è sicuro assumere questi sono problemi di copia/incolla, non veri errori di sintassi? – nrabinowitz

+0

La copia/incolla della colpa, la logica sottostante, si spera ancora venga trasmessa. Grazie per la segnalazione. – aztechy

risposta

8

Hai un sacco di codice mancante nell'esempio, ma se ho capito bene, il problema è che stai provando a visualizzare le viste che dipendono dall'HTML generato da LayoutView, ma il layout viene scelto in modo asincrono quando il il modello è aggiornato, quindi LayoutView non è stato ancora reso.

ci sono (come con praticamente Backbone legate tutto) più approcci a questo, ma in generale:

  • Se ho vista che dipendono da un "padre" vista in fase di rendering, io attribuire la responsabilità per l'istanziazione e il rendering di tali viste nella vista principale, in questo caso LayoutView, non MainView.

  • Se il genitore viene reso in modo asincrono, è necessario eseguire tale istanziazione nel callback - qui, il metodo LayoutView.render().

quindi potrei strutturare il codice come questo:

LayoutView = Backbone.view.extend({ 
    initialize: function() { 
    this.model.bind('change:version', this.render, this); 
    // you probably want to call this.model.fetch() here, right? 
    }, 
    templateA: _.template($('#main-template').html()), 
    templateB: _.template($('#secondary-template').html()), 
    render: function() { 
    if ('main' == this.model.get('version')) { 
     this.el.html(this.templateA()); 
    } else { 
     this.el.html(this.templateB()); 
    } 
    // now instantiate and render subviews 
    this.graphView = new GraphView({ 
     // note that I'm using this.$ to help scope the element 
     el: this.$('.graphContainer'), 
     model: graph 
    }); 
    this.tableView = new TableView({ 
     el: this.$('.dataTableContainer'), 
     model: dataSet 
    }); 
    // you probably need to call graphView.render() 
    // and tableView.render(), unless you do that 
    // in their initialize() functions 
    }, 
}); 

Si noti che non c'è bisogno di passare in un el a esemplificazione - si potrebbe istanziare subviews in initialize(), e imposta la proprietà el in subview.render(). È anche possibile creare un'istanza in MainView.initialize(), come ora, e passare le viste a LayoutView per renderizzare in modo asincrono. Ma penso che l'approccio sopra sia probabilmente più pulito.

+0

Grazie per la risposta.Ho giocato con questa implementazione di chiamare le subviews all'interno di LayoutView dopo il processo di rendering. Hai ragione nell'assumere che le varie sottoview abbiano il proprio rendering, che viene aggiornato sugli stati di modifica del modello. Per qualche strana ragione, anche se sembrava dopo il mio recupero, alcune chiamate di rendering venivano chiamate più volte. Mi ha fatto pensare che stavo aggiungendo in modo inefficiente le varie sottoview più volte. Immagino che dovrei solo rintracciare cos'altro potrebbe causare le chiamate di rendering multiple dopo aver recuperato i miei dati. Grazie ancora! – aztechy

+0

Sono contento che questo mi sia stato d'aiuto - spesso trovo che ho bisogno della logica per creare un'istanza di una sottoview solo se non già caricata, il che potrebbe essere il problema. (A proposito, se ti piace la risposta, puoi approvarla premendo il segno di spunta a sinistra :) – nrabinowitz

+0

Esatto, creare la logica per creare un'istanza è necessaria la visualizzazione secondaria. Sfortunatamente sto incontrando un problema per cui l'istanziazione delle sottoview all'interno di layoutView.render() sta causando delle sottoview che digitano gli eventi di modifica del modello, definiti nella loro configurazione di inizializzazione, per non renderizzare correttamente. Principalmente nel caso in cui lo stato del modello di una vista rimanga lo stesso per tutta la navigazione, quella particolare sotto-sezione sembra essere sovrascritta con un modello vuoto a causa del processo sopra riportato. Sto pensando che questo possa richiedere la rielaborazione della logica dell'evento. – aztechy