2012-03-09 1 views
17

Ho uno scenario in cui i dati manipolati sul client vengono presentati e interagiti in modo diverso rispetto a quello che è rappresentato sul server.Proprietà calcolate in Backbone

Considerare la seguente risorsa event restituita dal server.

{ 
    "id": 123, 
    "start_at": 1331336004906, 
    "end_at": 1331337704906 
} 

E il seguente modello per la modifica:

<form> 
    <!-- Notice how date and time are separated in the interface --> 
    <input type="text" name="start_date" value="{{start_date}}" /> 
    <input type="text" name="start_time" value="{{start_time}}" /> 

    <!-- Instead of asking for an end date/time, we ask for the duration --> 
    <input type="text" name="duration" value="{{duration}}" /> 

    <input type="submit" /> 
</form> 

Come potrei fare per il trattamento start_date, start_time, e duration come attributi nel mio modello Backbone senza inviarli al server? Dovrei modificare .toJSON()?

+0

Qual è il problema con il formato ressource evento, arent hanno timestamp? – mpm

+0

Vedi anche: http://stackoverflow.com/questions/10648990/how-to-access-a-calculated-field-of-a-backback-model-from-handlebars-template –

risposta

4

Il modello deve corrispondere il più strettamente possibile a ciò che si ha sul lato server. Quindi attenersi a start_at e end_at. Ciò semplificherà notevolmente le operazioni di sync().

Su vista del modulo di modifica, è possibile:

  1. Compute start_date, start_time, duration attraverso funzioni semplici e li chiamano nel modello.
  2. Converti in start_at e end_at nell'invio.
+1

Non sta mettendo questo nella View andando a portare a un sacco di codice ridondante? Userò gli stessi attributi virtuali in tutto il sito, dove un singolo evento ha più viste che lo mostrano. – bloudermilk

+0

Nessuna ridondanza necessaria. Crea una vista di base ed estendila. – ggozad

+1

Buona chiamata! Penso anche che lo schema del decoratore potrebbe adattarsi a questo problema. – bloudermilk

6

Siamo molto abituati a trasmettere model.toJSON()-alimentare il modello. E questo metodo è molto difficile da sovrascrivere perché viene utilizzato da altri componenti.

Ma possiamo alimentare il modello con un metodo personalizzato model.toJSONDecorated() che può apparire come segue:

# in your Backbone.Model 
toJSONDecorated: function(){ 
    return 
    _.extend( 
     this.toJSON(), 
     { 
     start_date : Utils.dateFromDate(this.get("start_at")), 
     start_time : Utils.timeFromDate(this.get("start_at")), 
     duration : Utils.youGetTheIdea(:)) 
     } 
    ); 
} 

Naturalmente questo sta rompendo alcuni modelli, posso vivere con esso, se non è possibile sposta questa logica in una classe Decorator come suggerito da altre persone in altre risposte.

5

avete alcune opzioni:

  • Override toJSON per restituire l'calcolata duration

  • Creare un metodo duration() sul Backbone Model. L'unico inconveniente è che devi chiamarlo in modo diverso (obj.duration() anziché obj.get('duration')). Secondo lei che le mani obj.toJSON() al modello, mescolare in l'attributo duration

  • Usa https://github.com/asciidisco/Backbone.Mutators (o simile) per creare un getter derivato per la durata

+0

Grazie. Di solito adotto l'approccio descritto, ma non conoscevo i Mutator: sembra molto utile e ben fatto. –

22

Sto usando una combinazione della initialize() funzionano insieme ai listener di eventi change per aggiornare gli attributi derivati. L'idea è innanzitutto quella di calcolare gli attributi sull'inizializzazione del modello e in secondo luogo lasciare che il modello ascolti le proprie modifiche e aggiorni gli attributi di conseguenza.

La mia soluzione si presenta più o meno così:

MyModel: Backbone.Model.extend({ 
    initialize: function() { 
     this.updateDerivedAttributes(); 
     this.on('change:start_at', this.updateDerivedAttributes, this); 
    }, 
    updateDerivedAttributes: function() { 
     this.set({ 
      start_date: Utils.dateFromDate(this.get("start_at")), 
      start_time: Utils.timeFromDate(this.get("start_at")), 
      duration: Utils.youGetTheIdea() 
     }, {silent:true}); 
    } 
});