Costruisci un gestore di contatti usando Backbone.js Parte 2

Bentornati alla seconda parte di questo tutorial; nella prima parte abbiamo esaminato alcune delle nozioni di base su modello, raccolta e visualizzazione per quando si lavorava con Backbone e ho visto come eseguire il rendering di singole viste di contatto utilizzando una vista principale associata a una raccolta.

In questa parte del tutorial, esamineremo come possiamo filtrare la nostra vista in base all'input dell'utente e come possiamo aggiungere un router per fornire alla nostra applicazione di base alcune funzionalità URL.
Avremo bisogno dei file di origine dalla prima parte mentre stiamo costruendo il codice esistente per questa parte. Consiglio vivamente di leggere la prima parte se non l'hai già fatto.


Reagendo all'input dell'utente

Potresti aver notato nella prima parte che ognuno dei nostri modelli ha un tipo chiamato attribuito che classifica ciascun modello in base al fatto che si riferisca ad un amico, un familiare di un collega. Aggiungiamo un elemento select alla nostra vista principale che consentirà all'utente di filtrare i contatti in base a questi tipi.

Ora, possiamo hardcode un menu di selezione nel nostro HTML sottostante e aggiungere manualmente le opzioni per ciascuno dei diversi tipi. Ma questo non sarebbe molto lungimirante; e se aggiungessimo un nuovo tipo più tardi o elimineremo tutti i contatti di un certo tipo? La nostra applicazione non ha ancora la capacità di aggiungere o rimuovere contatti (avviso di spoiler della terza parte!), Ma è ancora meglio prendere in considerazione questo tipo di cose, anche in questa fase iniziale della nostra applicazione.

Pertanto, possiamo facilmente creare un elemento di selezione in modo dinamico in base ai tipi esistenti. Prima aggiungeremo un minimo di HTML alla pagina sottostante; aggiungi i seguenti nuovi elementi al contenitore dei contatti:

 

Ecco, abbiamo un esterno elemento per agire come un contenitore generale, all'interno del quale è un altro contenitore con un id attributo e a con qualche testo esplicativo.

Ora costruiamo il ", html:""); _.each (this.getTypes (), function (item) var option = $ ("

Il primo dei nostri metodi, GetTypes () restituisce una matrice creata usando Underscore uniq () metodo. Questo metodo accetta un array come argomento e restituisce un nuovo array contenente solo oggetti unici. La matrice che passiamo nel uniq () metodo viene generato utilizzando Backbone cogliere () metodo, che è un modo semplice per estrarre tutti i valori di un singolo attributo da una collezione di modelli. L'attributo a cui siamo interessati qui è il genere attributo.

Al fine di prevenire problemi di casi in seguito, dovremmo anche normalizzare i tipi in lettere minuscole. Possiamo usare una funzione iteratrice, fornita come terzo argomento uniq (), per trasformare ogni valore prima che sia messo attraverso il comparatore. La funzione riceve l'argomento corrente come argomento, quindi restituiamo l'elemento in formato minuscolo. Il secondo argomento è passato a uniq (), che abbiamo impostato falso qui, è un flag utilizzato per indicare se l'array confrontato è stato ordinato.

Il secondo metodo, createSelect () è leggermente più grande, ma non molto più complesso. Il suo unico scopo è creare e restituire un nuovo .

Per rendere effettivamente il elemento, con un'opzione per ciascuno dei diversi tipi di contatto:


Filtro della vista

Quindi ora abbiamo il nostro menu, possiamo aggiungere la funzionalità per filtrare la vista quando un'opzione è selezionata. Per fare ciò, possiamo utilizzare le viste principali eventi attributo per aggiungere un gestore di eventi UI. Aggiungi il seguente codice direttamente dopo il nostro renderSelect () metodo:

eventi: "cambia # filtro seleziona": "setFilter",

Il eventi l'attributo accetta un oggetto di Key: Value accoppia in cui ogni chiave specifica il tipo di evento e un selettore a cui associare il gestore eventi. In questo caso siamo interessati a modificare evento che verrà licenziato dal elemento all'interno del #filtro contenitore. Ogni valore nell'oggetto è il gestore di eventi che deve essere associato; in questo caso specificiamo setFilter come gestore.

Successivamente possiamo aggiungere il nuovo gestore:

setFilter: function (e) this.filterType = e.currentTarget.value; this.trigger ( "cambiamento: FilterType"); ,

Tutto ciò che dobbiamo fare nel setFilter () la funzione è impostata su una proprietà sulla vista principale chiamata Filtertype, che impostiamo sul valore dell'opzione selezionata, che è disponibile tramite currentTarget proprietà dell'oggetto evento che viene automaticamente passata al nostro gestore.

Una volta che la proprietà è stata aggiunta o aggiornata, possiamo anche attivare una custom modificare evento per esso utilizzando il nome della proprietà come spazio dei nomi. Vedremo come possiamo utilizzare questo evento personalizzato in un attimo, ma prima di farlo, possiamo aggiungere la funzione che eseguirà effettivamente il filtro; dopo il setFilter () metodo aggiungi il seguente codice:

filterByType: function () if (this.filterType === "all") this.collection.reset (contacts);  else this.collection.reset (contacts, silent: true); var filterType = this.filterType, filtered = _.filter (this.collection.models, function (item) return item.get ("type"). toLowerCase () === filterType;); this.collection.reset (filtrato); 

Per prima cosa controlliamo se la vista principale è Filtertype la proprietà è impostata su tutti; se lo è, semplicemente ripopolare la raccolta con il set completo di modelli, i cui dati sono memorizzati localmente sul nostro contatti schieramento.

Se la proprietà non è uguale tutti, abbiamo comunque ripristinato la raccolta per recuperare tutti i contatti nella raccolta, necessaria per passare da un tipo di contatto all'altro, ma questa volta impostiamo silenzioso opzione a vero (vedrai perché questo è necessario in un momento) in modo che il reset l'evento non è stato licenziato.

Quindi memorizziamo una versione locale delle viste Filtertype proprietà in modo che possiamo fare riferimento a una funzione di callback. Usiamo Underscore's filtro() metodo per filtrare la collezione di modelli. Il filtro() metodo accetta l'array per filtrare e una funzione di callback da eseguire per ogni elemento dell'array che viene filtrato. La funzione di callback è passata alla voce corrente come argomento.

La funzione di callback tornerà vero per ogni oggetto che ha un genere attributo uguale al valore che abbiamo appena memorizzato nella variabile. I tipi vengono nuovamente convertiti in lettere minuscole, per lo stesso motivo di prima. Qualsiasi elemento restituito dalla funzione di callback falso perché vengono rimossi dall'array.

Una volta che l'array è stato filtrato, chiamiamo il reset() metodo ancora una volta, passando nell'array filtrato. Ora siamo pronti per aggiungere il codice che collegherà il setType () metodo, il Filtertype proprietà e filterByType () metodo.


Eventi vincolanti per la raccolta

Oltre agli eventi UI vincolanti per la nostra interfaccia utilizzando il eventi attributo, possiamo anche associare i gestori di eventi alle raccolte. Nel nostro setFilter () metodo abbiamo generato un evento personalizzato, ora abbiamo bisogno di aggiungere il codice che legherà il filterByType () metodo per questo evento; aggiungi il seguente codice al inizializzare() metodo della nostra vista principale:

this.on ("change: filterType", this.filterByType, this);

Usiamo Backbone sopra() metodo per ascoltare il nostro evento personalizzato. Specifichiamo il filterByType () metodo come funzione di gestore per questo evento utilizzando il secondo argomento di sopra(), e può anche impostare il contesto per la funzione di callback impostando Questo come il terzo argomento. Il Questo l'oggetto qui si riferisce alla nostra vista principale.

Nel nostro filterByType funzione, ripristiniamo la raccolta per ripopolarla con tutti i modelli o con i modelli filtrati. Possiamo anche legarci al reset evento per ripopolare la raccolta con istanze di modello. Possiamo anche specificare una funzione di gestore per questo evento, e il bello è che abbiamo già ottenuto la funzione. Aggiungere la seguente riga di codice direttamente dopo modificare associazione degli eventi:

this.collection.on ("reset", this.render, this);

In questo caso stiamo ascoltando il reset evento e la funzione che vogliamo invocare è quella della collezione render () metodo. Specifichiamo anche che la callback dovrebbe usare Questo (come nell'istanza della vista principale) come contesto in cui viene eseguita. Se non forniamo Questo come terzo argomento, non saremo in grado di accedere alla raccolta all'interno di render () metodo quando gestisce il reset evento.

A questo punto, dovremmo ora trovare che possiamo usare la casella di selezione per visualizzare i sottoinsiemi dei nostri contatti. Il motivo per cui abbiamo impostato il silenzioso opzione per vero nel nostro filterByType () il metodo è tale che la vista non viene ridisegnata inutilmente quando reimposta la raccolta all'inizio del secondo ramo del condizionale. Dobbiamo fare in modo che possiamo filtrare in base a un tipo e quindi filtrare in base a un altro tipo senza perdere alcun modello.


Routing

Quindi, quello che abbiamo ottenuto finora va bene, possiamo filtrare i nostri modelli usando la casella di selezione. Ma non sarebbe fantastico se potessimo filtrare la collezione usando anche un URL? Il modulo router di Backbone ci offre questa capacità, vediamo come, e grazie al modo ben disaccoppiato con cui abbiamo strutturato il nostro filtro fino ad ora, è davvero facile aggiungere questa funzionalità. Per prima cosa dobbiamo estendere il modulo del router; aggiungi il seguente codice dopo la vista principale:

var ContactsRouter = Backbone.Router.extend (routes: "filter /: type": "urlFilter", urlFilter: function (type) directory.filterType = type; directory.trigger ("change: filterType"); );

La prima proprietà che definiamo nell'oggetto è passata al router estendere() il metodo è itinerari, che dovrebbe essere un oggetto letterale in cui ogni chiave è un URL da abbinare e ogni valore è una funzione di callback quando l'URL è abbinato. In questo caso stiamo cercando gli URL che iniziano con #filtro e termina con qualsiasi altra cosa. La parte dell'URL dopo il filtro/ parte viene passata alla funzione che specifichiamo come funzione di callback.

All'interno di questa funzione impostiamo o aggiorniamo il Filtertype proprietà della vista principale e quindi attivare la nostra abitudine modificare evento ancora una volta. Questo è tutto ciò che dobbiamo fare per aggiungere funzionalità di filtro usando l'URL. Abbiamo comunque bisogno di creare un'istanza del nostro router, che possiamo fare aggiungendo la seguente riga di codice direttamente dopo DirectoryView esemplificazione:

var contactsRouter = new ContactsRouter ();

Ora dovremmo essere in grado di inserire un URL come # Filtro / famiglia e la vista verrà nuovamente visualizzata per mostrare solo i contatti con la famiglia di tipi:

Quindi è fantastico, vero? Ma manca ancora una parte: come faranno gli utenti a usare i nostri piacevoli URL? Abbiamo bisogno di aggiornare la funzione che gestisce gli eventi dell'interfaccia utente sul elemento in modo che l'URL venga aggiornato quando viene utilizzata la casella di selezione.

Per fare questo richiede due passaggi; prima di tutto dovremmo abilitare il supporto della cronologia di Backbone avviando il servizio di cronologia dopo l'inizializzazione della nostra app; aggiungi la seguente riga di codice alla fine del nostro file di script (direttamente dopo aver inizializzato il nostro router):

Backbone.history.start ();

Da questo momento in poi, Backbone monitorerà l'URL per le modifiche hash. Ora, quando vogliamo aggiornare l'URL dopo che qualcosa accade, chiamiamo semplicemente il navigare() metodo del nostro router. Cambiare il filterByType () metodo in modo che appaia così:

filterByType: function () if (this.filterType === "all") this.collection.reset (contacts); contactsRouter.navigate ( "filtro / all");  else this.collection.reset (contacts, silent: true); var filterType = this.filterType, filtered = _.filter (this.collection.models, function (item) return item.get ("type") === filterType;); this.collection.reset (filtrato); contactsRouter.navigate ("filter /" + filterType); 

Ora, quando la casella di selezione viene utilizzata per filtrare la raccolta, l'URL verrà aggiornato e l'utente potrà quindi aggiungere un segnalibro o condividere l'URL, mentre i pulsanti Indietro e Avanti del browser navigeranno tra gli stati. Poiché la versione 0.5 Backbone ha anche supportato l'API pushState, tuttavia, affinché funzioni correttamente, il server deve essere in grado di eseguire il rendering delle pagine richieste, che non sono state configurate per questo esempio, quindi utilizzando il modulo di cronologia standard..


Sommario

In questa parte del tutorial, abbiamo esaminato un paio di altri moduli Backbone, in particolare i moduli Router, History ed Events. Abbiamo esaminato tutti i diversi moduli forniti con Backbone.

Abbiamo anche esaminato alcuni altri metodi Underscore, tra cui filtro(), che abbiamo usato per filtrare la nostra collezione solo per quei modelli contenenti un tipo specifico.

Infine, abbiamo esaminato il modulo Router di Backbone, che ci ha permesso di impostare percorsi che possono essere abbinati dalla nostra applicazione per attivare metodi e il modulo Cronologia che possiamo usare per ricordare lo stato e mantenere l'URL aggiornato con frammenti di hash.

Un punto da togliere è la natura liberamente accoppiata della nostra funzionalità di filtraggio; quando abbiamo aggiunto il filtro tramite il menu di selezione, è stato fatto in modo tale che fosse molto veloce e facile da seguire in seguito e aggiungere un metodo di filtraggio completamente nuovo senza dover cambiare il nostro filtro() metodo. Questa è una delle chiavi per creare con successo applicazioni JavaScript non banali, gestibili e scalabili. Se lo volessimo, sarebbe molto facile aggiungere un altro metodo di filtro completamente nuovo, che deve cambiare il nostro metodo di filtraggio.

Nella parte successiva di questa serie, torneremo a lavorare con i modelli e vediamo come rimuovere i modelli e aggiungerne di nuovi alla raccolta.