2012-06-18 3 views
13

Per quanto riguarda il nuovo sistema di routing Ember.js (descritto here), se ho capito bene, viste vengono distrutte quando si esce di un percorso.Come * non * a distruggere View quando si esce un percorso in Ember.js

C'è un modo per aggirare la distruzione delle viste all'uscita da una rotta, in modo che lo stato della vista venga preservato quando l'utente rientra nella rotta?


Aggiornamento: Sembra che, viste non vengono distrutti a meno che la vista di uscita è stato sostituito nel nuovo percorso. Ad esempio, se si è in stato A con ViewA in alcuni {{outlet master}} e si passa allo stato B con ViewB in {{outlet master}}, quindi ViewB sostituirà ViewA. Un modo per aggirare questo è definire più punti vendita quando è necessario conservare le viste, ad esempio {{outlet master1}}, {{outlet master2}}, ...

Una bella funzionalità sarebbe la possibilità di passare un array di punti di vista allo sbocco. Puoi anche scegliere se le viste verranno distrutte o semplicemente nascoste, una volta usciti da una rotta.

+5

Zack, penso che si può compiere quelle funzioni extra se fate la vostra presa di livello superiore di un 'ContainerView'. Quindi puoi manipolare direttamente i figli, tramite la proprietà 'childViews', e controllare se una vista figlio viene rimossa o semplicemente nascosta. –

risposta

9

Da allora ho capito come modificare il sistema di routing, in modo che le viste inserite nelle prese non vengano distrutte. In primo luogo sovrascrivo l'aiutante manubrio outlet, in modo che carica un Ember.OutletView in {{outlet}}:

Ember.Handlebars.registerHelper('outlet', function(property, options) { 
    if (property && property.data && property.data.isRenderData) { 
    options = property; 
    property = 'view'; 
    } 

    options.hash.currentViewBinding = "controller." + property; 

    return Ember.Handlebars.helpers.view.call(this, Ember.OutletView, options); 
}); 

Dove Ember.OutletView estende Ember.ContainerView come segue:

Ember.OutletView = Ember.ContainerView.extend({ 
    childViews: [], 

    _currentViewWillChange: Ember.beforeObserver(function() { 
     var childViews = this.get('childViews'); 

      // Instead of removing currentView, just hide all childViews 
      childViews.setEach('isVisible', false); 

    }, 'currentView'), 

    _currentViewDidChange: Ember.observer(function() { 
     var childViews = this.get('childViews'), 
      currentView = this.get('currentView'); 

     if (currentView) { 
      // Check if currentView is already within childViews array 
      // TODO: test 
      var alreadyPresent = childViews.find(function(child) { 
       if (Ember.View.isEqual(currentView, child, [])) {   
        return true; 
       } 
      }); 

      if (!!alreadyPresent) { 
       alreadyPresent.set('isVisible', true); 
      } else { 
       childViews.pushObject(currentView); 
      } 
     } 
    }, 'currentView') 

}); 

Fondamentalmente sovrascriviamo _currentViewWillChange() e solo nascondere tutti childViews invece di rimuovere il currentView. Quindi nel _currentViewDidChange() controlliamo se lo è già all'interno di childViews e agisce di conseguenza. Il Ember.View.isEqual è una versione modificata di Underscore isEqual:

Ember.View.reopenClass({ 
    isEqual: function(a, b, stack) { 
     // Identical objects are equal. `0 === -0`, but they aren't identical. 
     // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 
     if (a === b) return a !== 0 || 1/a == 1/b; 
     // A strict comparison is necessary because `null == undefined`. 
     if (a == null || b == null) return a === b; 
     // Unwrap any wrapped objects. 
     if (a._chain) a = a._wrapped; 
     if (b._chain) b = b._wrapped; 
     // Compare `[[Class]]` names. 
     var className = toString.call(a); 
     if (className != toString.call(b)) return false; 

     if (typeof a != 'object' || typeof b != 'object') return false; 
     // Assume equality for cyclic structures. The algorithm for detecting cyclic 
     // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 
     var length = stack.length; 
     while (length--) { 
      // Linear search. Performance is inversely proportional to the number of 
      // unique nested structures. 
      if (stack[length] == a) return true; 
     } 
     // Add the first object to the stack of traversed objects. 
     stack.push(a); 
     var size = 0, result = true; 
     // Recursively compare objects and arrays. 
     if (className == '[object Array]') { 
      // Compare array lengths to determine if a deep comparison is necessary. 
      size = a.length; 
      result = size == b.length; 
      if (result) { 
       // Deep compare the contents, ignoring non-numeric properties. 
       while (size--) { 
        // Ensure commutative equality for sparse arrays. 
        if (!(result = size in a == size in b && this.isEqual(a[size], b[size], stack))) break; 
       } 
      } 
     } else { 
      // Objects with different constructors are not equivalent. 
      if (a.get('constructor').toString() != b.get('constructor').toString()) { 
       return false; 
      } 

      // Deep compare objects. 
      for (var key in a) { 
       if (a.hasOwnProperty(key)) { 
        // Count the expected number of properties. 
        size++; 
        // Deep compare each member. 
        if (!(result = b.hasOwnProperty(key))) break; 
       } 
      } 
     } 
     // Remove the first object from the stack of traversed objects. 
     stack.pop(); 
     return result; 
    } 
}); 
+1

Un containerView è molto più adeguato invece di mettere tutta questa logica personalizzata in (che in un modo è una grossolana duplicazione di containerView). Questa non dovrebbe essere la soluzione accettata. – Valer

+1

Questa è una vista container e questa logica è purtroppo (ancora) necessaria per impedire la visualizzazione delle immagini. – runspired

4

In modo che lo stato della vista venga mantenuto quando l'utente rientra nella route .

Vorrei invece memorizzare tali informazioni nel controller (o nel gestore di stato) in modo che quando si reinserisce il percorso, la nuova vista venga inizializzata con il vecchio stato. Ha senso? Ad esempio, se si tratta di un elenco di post e uno è selezionato, si memorizzano i dati su quale post è stato selezionato nel controller (o nel gestore di stato). Dopo aver visitato un post specifico e poi tornare all'elenco, sarebbe stato selezionato lo stesso post.

Posso immaginare un caso d'uso in cui ciò non sarebbe molto utile (ad esempio lo scorrimento verso una posizione specifica in una lunga lista), quindi forse questo non risponde alla tua domanda.

+0

Sì, non sto parlando di una semplice selezione, ma di casi in cui potresti avere una forma lunga che l'utente ha parzialmente completato, facendo scorrere una lunga lista (come hai detto tu) ... Quindi non ha senso memorizza tutte queste informazioni nel controller e aggiorna la vista al reinserimento. Un altro modo è quello di memorizzare l'istanza View nel controller e utilizzarla ogni volta che si immette il percorso. Ma speravo che ci fosse un modo migliore. –

+0

Potrebbe ancora esserci un modo migliore: qualcuno che conosce Ember meglio di me (ce ne sono molti) potrebbe rispondere. :) – pjmorse