2012-06-18 41 views
13

關於新的Ember.js路由系統(描述爲here),如果我理解正確,當您退出路由時,視圖將被銷燬。在Ember.js中退出路由時,如何*不能*銷燬視圖

有沒有辦法在退出路由時繞過視圖的破壞,以便在用戶重新進入路由時保留視圖的狀態?


更新:看起來像,視圖不會被銷燬,除非在新路徑中替換出口視圖。例如,如果您在stateA中使用某個{{outlet master}}中的ViewA,並且您使用ViewB在{{outlet master}}中轉到stateB,則ViewB將替換ViewA。解決這個問題的方法是在需要保留視圖時定義多個插座,例如,{{outlet master1}},{{outlet master2}},...

一個很好的功能是能夠傳遞數組的意見,以出口。並且還可以選擇在退出路線時視圖是被銷燬還是被隱藏。

+5

扎克, 我認爲你可以完成這些額外的功能,如果你讓你的頂層出口處的'ContainerView'。然後,您可以通過'childViews'屬性直接操作它的子項,並且可以控制子視圖是被刪除還是隱藏。 –

回答

9

我已經弄清楚如何修改路由系統,以便插入插座的視圖不被破壞。首先我重寫車把outlet幫手,所以它加載一個Ember.OutletView{{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); 
}); 

Ember.OutletView延伸Ember.ContainerView如下:

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') 

}); 

我們基本上覆蓋_currentViewWillChange(),只是隱藏所有childViews而不是取消的currentView。然後在_currentViewDidChange()我們檢查currentView是否已經在childViews之內,並據此採取行動。該Ember.View.isEqualUnderscore 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

一個containerView是更充分,而不是把所有這些自定義邏輯(這在一種方式是一個粗略的containerView複製)。這不應該是被接受的解決方案。 – Valer

+1

這是一個容器視圖,不幸的是(這個邏輯)仍然是防止視圖撕裂的必要條件。 – runspired

4

這樣當用戶重新輸入 路徑時,視圖的狀態會被保留。

相反,我會將該信息存儲在控制器(或狀態管理器)中,以便重新輸入路徑時,新視圖將用舊狀態初始化。那有意義嗎?因此,例如,如果它是一個帖子列表,並且選擇了一個帖子,則可以存儲有關在控制器(或狀態管理器)中選擇了哪個帖子的數據。在訪問特定的帖子然後回到列表中後,將選擇相同的帖子。

我可以想象一個用例,它不會非常有用(例如,滾動到長列表中的特定位置),所以也許這不會回答你的問題。

+0

是的,我不是在談論一個簡單的選擇,但是你可能有一個長的形式,用戶已經部分完成,滾動定位一個長列表(如你所說)...所以這是沒有意義的將所有信息存儲到控制器中並在重新輸入時更新視圖。另一種方法是將View實例存儲在控制器中,並在每次輸入路由時使用該實例。但我希望有更好的辦法。 –

+0

還有更好的辦法:比我更瞭解安博(有很多)的人可能會回答。 :) – pjmorse