2015-11-17 34 views
5

我似乎經常在渲染視圖的情況下結束,但視圖所依賴的模型尚未加載。大多數情況下,我只是從網址獲取模型ID,例如對於一個假想的市場應用,與該URL的應用程序用戶的土地:在哪一點獲取視圖依賴的骨幹模型?

http://example.org/#/products/product0

在我ProductView,我創建了一個ProductModel,並設置其ID,product0然後我fetch()。我使用佔位符呈現一次,並在抓取完成時重新呈現。但我希望有更好的方法。

等待模型加載之前,渲染任何感覺沒有反應。重新渲染會導致閃爍,並且添加「正在加載...請稍候」或各處的旋鈕會使視圖模板變得非常複雜(尤其是如果模型提取失敗,因爲模型不存在或者用戶無權查看這一頁)。

那麼,當你還沒有模型時渲染視圖的正確方法是什麼?

我需要離開 從hashtag-views和使用pushState?服務器可以給我推嗎?我都是耳朵。從已經加載的頁面

加載:

我覺得還有更多的時候就已經有裝載作爲反對Product頁面上直接登陸頁面,你可以做。

如果應用程序呈現指向產品頁面的鏈接,比如說呈現ProductOrder集合,那麼還有什麼可以做的嗎?

<ul id="product-order-list"> 
    <li>Ordered 5 days ago. Product 0 <a href="#/products/product0">(see details)</a></li> 
    <li>Ordered 1 month ago. Product 1 <a href="#/products/product1">(see details)</a></li> 
</ul> 

我來處理這個鏈接到細節頁模式自然的方式是定義做這些方針路線:

routes: { 
    'products/:productid': 'showProduct' 
    ... 
} 

showProduct: function (productid) { 
    var model = new Product({_id: productid}); 
    var view = new ProductView({model: model}); 
    //just jam it in there -- for brevity 
    $("#main").html(view.render().el); 
} 

我傾向於再調用fetch()內部視圖的initialize功能,並從this.listenTo('change', ...)事件偵聽器調用this.render()。這導致了複雜的render()個案,並且對象出現並從視圖中消失。例如,Product的我的視圖模板可能會爲用戶評論保留一些屏幕空間,但是當且僅當註釋在產品上存在/啓用 - 並且在模型從服務器完全獲取之前通常不知道。

現在,哪裏/何時最適合進行抓取?

  1. 如果我在頁面轉換之前加載模型,會導致直觀的視圖代碼,但會引入用戶可感知的延遲。用戶將點擊列表中的一個項目,並且必須等待(無需更換頁面)模型才能返回。響應時間很重要,而且我沒有對此進行可用性研究,但我認爲用戶一旦點擊鏈接就會立即看到頁面發生變化。

  2. 如果我打開裏面的ProductViewinitialize,與this.model.fetch()和監聽模型事件,我不得不模型渲染兩次, - 與空的佔位符前一次(因爲否則的話,你必須在一個白色的頁面盯着),以及之後。如果加載過程中發生錯誤,那麼我必須擦除視圖(顯示閃爍/閃爍)並顯示一些錯誤。

是否有另一種選擇,我沒有看到,也許涉及一個可以在視圖之間重用的過渡加載頁面?或者是一個很好的做法,總是讓第一個電話打到render()顯示一些spinners/loading指標?

編輯:通過collection.fetch()

一個裝載可以認爲,由於該項目是已經上市的集合(用於渲染的鏈接列表中的集合)的一部分,他們可以獲取鏈接被點擊之前,與collection.fetch()。如果該集合確實是Product的集合,那麼呈現產品視圖將很容易。但是,用於生成列表的Collection可能不是ProductCollection。它可能是一個ProductOrderCollection或其他簡單地引用產品id(或一些足夠數量的產品信息來呈現鏈接)的參考。

通過collection.fetch()抓取所有Product也可能過高,如果Product模型大,ESP。在其中一個產品鏈接被點擊的可能性之外。

雞還是雞蛋? collection.fetch()方法也不能真正解決直接導航到產品頁面的用戶的問題......在這種情況下,我們仍然需要渲染一個ProductView頁面,該頁面需要Product模型從一個id(或任何內容產品頁面URL)。

回答

3

好的,所以在我看來有很多方法可以解決這個問題。我會列出所有我想到的,希望能夠與您合作,或者至少能夠激發您找到最佳解決方案。

我並不完全反對TJ的回答。如果您只是在網站加載時對所有產品執行collection.fetch()操作(用戶通常希望涉及一些加載時間),那麼您就擁有了所有數據,並且只需將數據傳遞給他提到。他建議的和我通常做的之間唯一的區別是,我通常在我所有的觀點中都有對應用程序的引用。所以,例如在我的初始化功能app.js我會做這樣的事情。

initialize: function() { 
    var options = {app: this} 

    var views = { 
     productsView: new ProductsView(options) 
    }; 

    this.collections = { 
     products: new Products() 
    } 

    // This session model is just a sandbox object that I use 
    // to store information about the user's session. I would 
    // typically store things like currentlySelectedProductId 
    // or lastViewedProduct or things like that. Then, I just 
    // hang it off the app for ease of access. 
    this.models = { 
     session: new Session() 
    } 
} 

然後在我的productsView.js初始化功能我這樣做:

initialize: function(options) { 
    this.app = options.app; 

    this.views = { 
     headerView: new HeaderView(options), 
     productsListView: new ProductsListView(options), 
     footerView: new FooterView(options) 
    }; 
} 

,我在productsView.js初始化創建的子視圖是任意的。我主要是試圖證明我繼續將該選項對象傳遞給視圖的子視圖。

這樣做是否允許每個視圖,無論是頂層視圖還是深層嵌套的子視圖,每個視圖都知道每個其他視圖,並且每個視圖都具有對應用程序數據的引用。

這兩個代碼示例還介紹了儘可能精確地確定功能範圍的概念。不要試圖去看待一切。將功能傳遞給其他視圖,以便每個視圖都有一個特定的用途。這將促進視圖的重用。特別複雜的模式。

現在回到實際的話題。如果您打算繼續前面加載所有產品,您應該在哪裏獲取它們?因爲就像你說過的,你不想讓一個空白頁面坐在你的用戶面前。所以,我的建議是欺騙他們。儘可能多地加載頁面,並且只阻止需要加載數據的部分。對用戶來說,這個頁面看起來像是在加載,而你實際上正在幕後進行工作。如果您可以誘使用戶認爲頁面正在穩定加載,那麼他們不太可能對頁面加載不耐煩。

因此,引用來自productsView.js的初始化,您可以繼續並讓headerView和footerView呈現。然後,您可以在productsListView的呈現中執行您的獲取。

現在,我知道你在想什麼。我失去了主意嗎?如果您在渲染函數中執行提取操作,那麼在我們觸及實際呈現productsViewList模板的那一行之前,調用將沒有時間返回。好吧,幸運的是,有幾種方法。一種方法是使用Promises。但是,我通常這樣做的方式就是使用render函數作爲自己的回調函數。我來給你展示。

render: function(everythingLoaded) { 
    var _this = this; 

    if(!!everythingLoaded) { 
     this.$el.html(_.template(this.template(this))); 
    } 
    else { 
     // load a spinner template here if you want a spinner 

     this.app.collection.products.fetch() 
      .done(function(data) { 
       _this.render(true); 
      }) 
      .fail(function(data) { 
       console.warn('Error: ' + data.status); 
      }); 
    } 

    return this; 
} 

現在,通過這種結構化渲染,實際模板將不會加載,直到數據完全加載。

雖然我們在這裏有渲染功能,但我想介紹一下我在哪裏使用的另一個概念。我稱之爲postRender。這是一個函數,當模板加載完成後,我執行任何依賴於DOM元素的代碼。如果你只是編寫一個普通的.html頁面,那麼這是傳統上在$(document).ready(function(){})中的代碼;。值得注意的是我沒有爲我的模板使用.html文件。我使用嵌入式JavaScript文件(.ejs)。接下來,postRender函數是我已經基本添加到我的鍋爐板代碼中的一個函數。因此,無論何時,我都會在代碼庫中調用渲染來查看視圖,我立即將postRender鏈接到它。我也像使用渲染一樣使用postRender作爲自己的回調。所以,基本上以前的代碼示例在我的代碼庫中看起來像這樣。

render: function(everythingLoaded) { 
    var _this = this; 

    if(!!everythingLoaded) { 
     this.$el.html(_.template(this.template(this))); 
    } 
    else { 
     // load a spinner template here if you want a spinner 

     this.app.collection.products.fetch() 
      .done(function(data) { 
       _this.render(true).postRender(true); 
      }) 
      .fail(function(data) { 
       console.warn('Error: ' + data.status); 
      }); 
    } 

    return this; 
}, 

postRender: function(everythingLoaded) { 
    if(!!everythingLoaded) { 
     // do any DOM manipulation to the products list after 
     // it's loaded and rendered 
    } 
    else { 
     // put code to start spinner 
    } 

    return this; 
} 

通過像這樣鏈接這些函數,我們保證它們將按順序運行。

============================================== ===========================

所以,這是解決問題的一種方法。但是,您提到您不希望預先加載所有產品,因爲擔心請求可能需要很長時間。


側面說明:你真的應該考慮購買與產品有關的任何信息,請致電可能導致調用花費相當長的時間,使大塊的信息的單獨請求。我有一種感覺,如果用戶能夠快速獲得核心信息,並且與每個產品相關的縮略圖需要稍長的時間才能加載,那麼用戶將會更加容忍數據需要加載一段時間才能加載。世界。這只是我的看法。


另一種方式來解決這個問題,如果你只想去一個特定的產品頁面,然後只需實現渲染/ postRender模式我上述對個人的ProductView。但是請注意,您的productView.js可能要看起來像這樣:

initialize: function(options) { 
    this.app = options.app; 
    this.productId = options.productId; 

    this.views = { 
     headerView: new HeaderView(options), 
     productsListView: new ProductsListView(options), 
     footerView: new FooterView(options) 
    }; 
} 

render: function(everythingLoaded) { 
    var _this = this; 

    if(!!everythingLoaded) { 
     this.$el.html(_.template(this.template(this))); 
    } 
    else { 
     // load a spinner template here if you want a spinner 

     this.app.collection.products.get(this.productId).fetch() 
      .done(function(data) { 
       _this.render(true).postRender(true); 
      }) 
      .fail(function(data) { 
       console.warn('Error: ' + data.status); 
      }); 
    } 

    return this; 
}, 

postRender: function(everythingLoaded) { 
    if(!!everythingLoaded) { 
     // do any DOM manipulation to the product after it's 
     // loaded and rendered 
    } 
    else { 
     // put code to start spinner 
    } 

    return this; 
} 

這裏唯一的區別是,產品ID是沿着選項傳遞對象來初始化,然後多數民衆贊成拉出使用在渲染函數中的.fetch中。

============================================== =========================== 總之,我希望這有助於。我不確定我是否回答了您的所有問題,但我認爲我對他們做了很好的傳球。爲了這個太久,我現在就要停下來,讓你消化這個問題,並提出你有任何問題。我想我可能不得不做至少1更新這篇文章進一步沖刷出來。

+0

我相信你所說的是延遲獲取是不可避免的,但是在加載模型時顯示旋鈕或處理錯誤的複雜性不是模板的一部分。你有三個不同的模板? - 一個用於成功,一個用於「加載調試器」的情況,另一個用於獲取失敗的情況?我開始相信沒有真正的乾淨解決方案。 'render()'中的承諾構造優雅。 'postRender'解決了一個不同的問題,即將子視圖嵌入到尚未附加到根DOM的父元素中。 –

+0

由於您設置的方式,是的,我相信延遲獲取是不可避免的。但是,我在很多地方都成功地做到了這一點。除非失敗模板變得複雜,否則我通常不會創建一個全新的失敗模板。相反,我通常會做的只是將代碼包裝在模板中的try/catch中,並且在catch中我會放入類似'

No data.
'的類似的東西,因爲當我嘗試訪問不存在的變量時,我將不可避免地遇到錯誤一些在我的模板中。再次,這是可能的,因爲我爲我的模板使用.ejs文件。 –

+0

另外,你是正確的,postRender完全解決了完全不同的問題。不過,我認爲這將防止未來的問題,所以我只是繼續前進,然後撒了它。但是,我會提到,如果你最終要做任何嵌套視圖,然後我會看[殭屍視圖](https:// lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/)並創建一個[assign function](http://ianstormtaylor.com/assigning-backbone-subviews訂做偶數清潔劑/)。這些是在我工作的代碼庫中遍佈各處的兩件事情。 –

2

你開始說:

我有一個郵件列表中的一個Collection視圖

那麼什麼是集合有..?楷模..!

當你做collection.fetch()你檢索所有的模型。

當用戶選擇一個項目,只是通過相應的模式,以項目看,是這樣的:

this.currentView = new ItemView({ 
    model: this.collection.find(id); // where this points to collection view 
          // and id is the id of clicked model 
}); 

這種方式,不會有任何延遲/不當的渲染。

如果您的收藏終點返回大量數據..?

然後執行常見的做法,如分頁,延遲加載等。


你建造一個產品型號與給定的ID

對我來說,這聽起來是錯誤的。如果您有一系列產品,則不應該手動構建這些模型。

讓集合在呈現列表視圖之前獲取模型。這樣你所提到的所有問題都可以避免。

+0

我喜歡你的答案,但我不認爲它適用於所有的時間。 隨着時間的推移,我的模特傾向於越來越多的領域。在某些情況下,通過集合獲取它們是沒有意義的,只有其中一個被點擊。 'Product'可能包含base64縮略圖,或者一些用戶的詳細信息。 一般來說,這是我認識到我的'模型'和'集合'的假設示例存在缺陷,我可以列出不是'產品'集合,而是「最近訂單」的集合,或者「產品發貨」,僅對產品id有參考。將編輯。 –