自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Ember.js 的視圖層

開(kāi)發(fā) 前端
本指導(dǎo)會(huì)詳盡闡述 Ember.js 視圖層的細(xì)節(jié)。為想成為熟練 Ember 開(kāi)發(fā)者準(zhǔn)備,且包 含了對(duì)于入門(mén) Ember 不必要的細(xì)節(jié)。

本指導(dǎo)會(huì)詳盡闡述 Ember.js 視圖層的細(xì)節(jié)。為想成為熟練 Ember 開(kāi)發(fā)者準(zhǔn)備,且包 含了對(duì)于入門(mén) Ember 不必要的細(xì)節(jié)。

Ember.js 有一套復(fù)雜的用于創(chuàng)建、管理并渲染連接到瀏覽器 DOM 上的層級(jí)視圖的系 統(tǒng)。視圖負(fù)責(zé)響應(yīng)諸如點(diǎn)擊、拖拽以及滾動(dòng)等的用戶(hù)事件,也在視圖底層數(shù)據(jù)變更時(shí)更 新 DOM 的內(nèi)容。

視圖層級(jí)通常由求值一個(gè) Handlebars 模板創(chuàng)建。當(dāng)模板求值后,會(huì)添加子視圖。當(dāng) 那些 子視圖求值后,會(huì)添加它們的子視圖,如此遞推,直到整個(gè)層級(jí)被創(chuàng)建。

即使你并沒(méi)有在 Handlebars 模板中顯式地創(chuàng)建子視圖,Ember.js 內(nèi)部仍使用視圖系 統(tǒng)更新綁定的值。例如,每個(gè) Handlebars 表達(dá)式 {{value}} 幕后創(chuàng)建一個(gè)視圖, 這個(gè)視圖知道當(dāng)值變更時(shí)如何更新綁定值。

你也可以在應(yīng)用運(yùn)行時(shí)用 Ember.ContainerView 類(lèi)對(duì)視圖層級(jí)做出修改。一個(gè)容器 視圖暴露一個(gè)可以手動(dòng)修改的子視圖實(shí)例數(shù)組,而非模板驅(qū)動(dòng)。

視圖和模板串聯(lián)工作提供一套用于創(chuàng)建任何你夢(mèng)寐以求的用戶(hù)界面的穩(wěn)健系統(tǒng)。最終用 戶(hù)應(yīng)從諸如當(dāng)渲染和事件傳播是的計(jì)時(shí)事件之類(lèi)的復(fù)雜東西中隔離開(kāi)。應(yīng)用開(kāi)發(fā)者應(yīng)可 以一次性把他們的 UI 描述成 Handlebars 標(biāo)記字符串,然后繼續(xù)完成他們的應(yīng)用,而 不必?zé)烙诖_保它一直是最新的。

它解決了什么問(wèn)題?

子視圖

在典型的客戶(hù)端應(yīng)用中,視圖同時(shí)在本身和 DOM 中表示嵌套的元素。在解決這個(gè)問(wèn)題 的天真方案中,獨(dú)立的視圖對(duì)象表示單個(gè) DOM 元素,專(zhuān)門(mén)的引用解決不同種類(lèi)的視圖 保持對(duì)概念中嵌套在它們內(nèi)部的視圖的跟蹤。

這里是一個(gè)簡(jiǎn)單的例子,表示一個(gè)應(yīng)用主視圖,里面有一集合嵌套視圖,且獨(dú)立的元素在集合內(nèi)嵌套。

 

 這個(gè)系統(tǒng)第一眼看上去毫無(wú)異樣,但是想象我們要在上午 8 點(diǎn)而不是上午 9 點(diǎn)開(kāi)放喬 的七鰓鰻小屋。在這種情況下,我們會(huì)想要重新渲染應(yīng)用視圖。因?yàn)殚_(kāi)發(fā)者需要構(gòu)建指 向在一個(gè)特殊基礎(chǔ)上的子視圖的引用,這個(gè)重渲染過(guò)程存在若干問(wèn)題。

為了重新渲染應(yīng)用視圖,應(yīng)用視圖也必須手動(dòng)重新渲染子視圖并重新把它們插入到應(yīng)用 視圖的元素中。如果實(shí)現(xiàn)得完美,這個(gè)過(guò)程會(huì)正常工作,但它依賴(lài)于一個(gè)完美的,專(zhuān)門(mén) 的視圖層級(jí)實(shí)現(xiàn)。如果任何一個(gè)視圖沒(méi)有精確地實(shí)現(xiàn)它,整個(gè)重新渲染過(guò)程會(huì)失敗。

為了避免這些問(wèn)題,Ember 的視圖層級(jí)從概念上就帶有子視圖的烙印。 

  

 當(dāng)應(yīng)用時(shí)圖重新渲染時(shí),Ember 而不是應(yīng)用代碼負(fù)責(zé)重新渲染并插入子視圖。這也意味 著 Ember 可以為你執(zhí)行任何內(nèi)存管理,比如清理觀(guān)察者和綁定。

這不僅在一定程度上消滅了樣板代碼,也破除了有瑕疵的視圖層級(jí)實(shí)現(xiàn)帶來(lái)的未期失敗的可能。

#p#

事件委派

在過(guò)去,web 開(kāi)發(fā)者已經(jīng)用在獨(dú)立的單個(gè)元素上添加事件監(jiān)聽(tīng)器來(lái)獲知什么時(shí)候用戶(hù)與 它們交互。例如,你會(huì)有一個(gè) <div> 元素,其上注冊(cè)了一個(gè)當(dāng)用戶(hù)點(diǎn)擊它時(shí)觸發(fā)的 函數(shù)。

盡管如此,這個(gè)途徑在處理大數(shù)量交互元素上不會(huì)縮放。比如,想象一個(gè)帶有 100 個(gè) <li><ul> ,每個(gè)項(xiàng)目后都有一個(gè)刪除按鈕。既然所有的這些項(xiàng)目行為都是一 致的,為每個(gè)刪除按鈕創(chuàng)建共計(jì) 100 個(gè)事件監(jiān)聽(tīng)器無(wú)疑是低效的。   

 

 

 

 

 

 

 

 要解決這個(gè)問(wèn)題,開(kāi)發(fā)者發(fā)現(xiàn)了一種名為“事件委派”的技術(shù)。你可以在容器元素上注冊(cè) 一個(gè)監(jiān)聽(tīng)器并使用 event.target 來(lái)識(shí)別哪個(gè)元素是用戶(hù)點(diǎn)擊的,而不是為問(wèn)題中的 每個(gè)項(xiàng)目創(chuàng)建一個(gè)監(jiān)聽(tīng)器。

 

 

 

 

 

 

實(shí)現(xiàn)這有一些微妙,因?yàn)橐恍┦录ū热?focus 、 blurchange )不會(huì)冒 泡。幸運(yùn)的是,jQuery 已經(jīng)徹底解決了這個(gè)問(wèn)題;用 jQuery 的 on 方法可以可靠 地處理所有原生瀏覽器事件。

其它 JavaScript 框架用兩種方法中的其一來(lái)處理這個(gè)問(wèn)題。第一種是,它們要你自己 實(shí)現(xiàn)原生解決方案,為每個(gè)項(xiàng)目創(chuàng)建獨(dú)立的視圖。當(dāng)你創(chuàng)建視圖,它在視圖的元素上設(shè) 置一個(gè)監(jiān)聽(tīng)器。如果你有一個(gè)含有 500 個(gè)項(xiàng)目的列表,你會(huì)創(chuàng)建 500 個(gè)視圖并且每個(gè) 視圖都會(huì)在它自己的元素上設(shè)置一個(gè)監(jiān)聽(tīng)器。

第二種方法是,框架在視圖層內(nèi)置事件委派。當(dāng)創(chuàng)建一個(gè)視圖,你可以提供一個(gè)事件列 表來(lái)在事件發(fā)生時(shí)委派一個(gè)方法來(lái)調(diào)用。這只剩下識(shí)別接受事件的方法的點(diǎn)擊上下文 (比如,列表中的哪個(gè)項(xiàng)目)。

你現(xiàn)在要面對(duì)兩個(gè)令人不安的選擇:為每個(gè)項(xiàng)目創(chuàng)建一個(gè)新視圖,這樣會(huì)喪失事件委派 的優(yōu)勢(shì),或是為所有項(xiàng)目創(chuàng)建單個(gè)視圖,這樣必須存儲(chǔ) DOM 中底層 JavaScript 的信息。

要解決這個(gè)問(wèn)題,Ember 用 jQuery 把所有事件委派到應(yīng)用的根元素(通常是文檔的 body )。當(dāng)一個(gè)事件發(fā)生,Ember 識(shí)別出最近的處理事件視圖并調(diào)用它的事件處理 器。這意味著你可以創(chuàng)建視圖來(lái)保存一個(gè) JavaScript 上下文,但仍然從事件委派上受 益。

進(jìn)一步地,因?yàn)?Ember 只為整個(gè) Ember 應(yīng)用注冊(cè)一個(gè)事件,創(chuàng)建新視圖永遠(yuǎn)都不需要 設(shè)置事件監(jiān)聽(tīng)器,這使得重渲染高效且免于出錯(cuò)。當(dāng)視圖有一個(gè)子視圖,這也意味著不 需要手動(dòng)取消委派重新渲染過(guò)程中替換掉的視圖。

渲染管道

大多數(shù) web 應(yīng)用用特殊的模板語(yǔ)言標(biāo)記來(lái)指定它們的用戶(hù)界面。對(duì)于 Ember.js,我們 已經(jīng)完成用可在值修改的時(shí)候自動(dòng)更新模板的 Handlebars 模板語(yǔ)言來(lái)編寫(xiě)模板。

雖然顯示模板的過(guò)程對(duì)開(kāi)發(fā)者是自動(dòng)的,但其遮蓋了把原始模板轉(zhuǎn)換為最終模板、生成 用戶(hù)可見(jiàn)的 DOM 表示的一系列必要步驟。

這是 Ember 視圖的近似生命周期: 

 

 

 

 

 

1. 模板編譯

應(yīng)用的模板通過(guò)網(wǎng)絡(luò)加載或以字符串形式作為應(yīng)用的載荷。當(dāng)應(yīng)用加載時(shí),它發(fā)送模板 字符串到 Handlebars 來(lái)編譯成函數(shù)。一經(jīng)編譯,模板函數(shù)會(huì)被保存,且可以被多個(gè)視 圖重復(fù)使用,每次都它們都需重新編譯。

這個(gè)步驟會(huì)在應(yīng)用中服務(wù)器預(yù)編譯模板的地方發(fā)出。在那些情況下,模板不作為原始的 人類(lèi)可讀的模板傳輸,而是編譯后的代碼。

因?yàn)?Ember 負(fù)責(zé)模板編譯,你不需要做任何額外的工作來(lái)保證編譯后的模板可以重用。

#p#

2. 字符串的連接

當(dāng)應(yīng)用在視圖上調(diào)用 appendappendTo 時(shí),一個(gè)視圖渲染過(guò)程會(huì)被啟動(dòng)。 appendappendChild 調(diào)用 安排 視圖渲染并在之后插入。這允許應(yīng)用中的 延遲邏輯(譬如綁定同步)在渲染元素之前執(zhí)行。

要開(kāi)始渲染過(guò)程,Ember 創(chuàng)建一個(gè) RenderBuffer 并把它呈遞給視圖來(lái)把視圖的內(nèi)容 附加到上面。在這個(gè)過(guò)程中,視圖可以創(chuàng)建并渲染子視圖。當(dāng)它這么做時(shí),父視圖創(chuàng)建 并分配一個(gè) RenderBuffer 給子視圖,并把它連接到父視圖的 RenderBuffer 上。

Ember 在渲染每個(gè)視圖前刷新綁定同步隊(duì)列。這樣,Ember 保障不會(huì)渲染需要立即替換 的過(guò)期數(shù)據(jù)。

一旦主視圖完成渲染,渲染過(guò)程會(huì)創(chuàng)建一個(gè)視圖樹(shù)(即“視圖層級(jí)”),連接到緩沖區(qū)樹(shù) 上。通過(guò)向下遍歷緩沖區(qū)樹(shù)并把它們轉(zhuǎn)換為字符串,我們就有了一個(gè)可以插入到 DOM 的字符串。

這里是一個(gè)簡(jiǎn)單的例子: 

 

 

 

 

除子節(jié)點(diǎn)之外(字符串和其它 RenderBuffer ), RenderBuffer 也會(huì)封裝元素標(biāo) 簽名稱(chēng)、id、class、樣式和其它屬性。這使得渲染過(guò)程修改這些屬性(例如樣式)成 為可能,即使在子字符串已經(jīng)渲染完畢。因?yàn)檫@些屬性的許多都可以通過(guò)綁定(例如用 bindAttr )控制,這使得渲染過(guò)程穩(wěn)健且透明。

3. 元素的創(chuàng)建和插入

在渲染過(guò)程的最后,根視圖向 RenderBuffer 請(qǐng)求它的元素。 RenderBuffer 獲得 它的完整字符串并用 jQuery 把它轉(zhuǎn)換成一個(gè)元素。視圖把那個(gè)元素分配到它的 element 屬性并把把它放置到 DOM 中正確的位置( appendTo 指定的位置,如果 應(yīng)用使用 append 即是應(yīng)用的根元素)。

雖然父視圖直接分配它的元素,但每個(gè)子視圖惰性查找它的元素。它通過(guò)查找 id 匹 配它的 elementId 屬性的元素來(lái)完成這。除非顯式提供,渲染過(guò)程生成一個(gè) elementId 屬性比你更分配它的值給視圖的 RenderBuffer ,RenderBuffer 允 許視圖按需查找它的元素。

4. 重新渲染

在視圖把自己插入到 DOM 后,Ember 和應(yīng)用都會(huì)要重新渲染視圖。它們可以在視圖上 調(diào)用 rerender 方法來(lái)出發(fā)一次重渲染。

重新渲染會(huì)重復(fù)上面的步驟 2 和步驟 3,有兩點(diǎn)例外:

  • rerender 用新元素替換已有的元素,而不是把元素插入到顯式定義的位置。
  • 除了渲染新元素,它也刪除舊元素并銷(xiāo)毀它的子元素。這允許 Ember 在重新渲染視 圖時(shí)自動(dòng)處理撤銷(xiāo)合適的綁定和觀(guān)察者。這使得路徑上的觀(guān)察者可行,因?yàn)樽?cè)和撤銷(xiāo) 注冊(cè)所有的嵌套觀(guān)察者都是自動(dòng)的。

最常見(jiàn)的導(dǎo)致視圖重新渲染的原因是當(dāng)綁定到 Handlebars 表達(dá)式( {{foo}} )變 更。Ember 內(nèi)部為每個(gè)表達(dá)式創(chuàng)建一個(gè)簡(jiǎn)單的視圖,并且在路徑上注冊(cè)一個(gè)觀(guān)察者。當(dāng) 路徑變更時(shí),Ember 用新值更新那個(gè)區(qū)域的 DOM。

另一個(gè)常見(jiàn)的情況是一個(gè) {{#if}}{{#with}} 塊。當(dāng)渲染一個(gè)模板時(shí),Ember 為這些塊輔助標(biāo)創(chuàng)建虛擬的視圖。這些虛擬的視圖不會(huì)出現(xiàn)在公共可訪(fǎng)問(wèn)的視圖層級(jí)里 (當(dāng)從視圖獲取 parentViewchildViews 時(shí)),但它們的存在啟用了一致的重 渲染。

當(dāng)傳遞到 {{#if}}{{#with}} 的路徑變更,Ember 自動(dòng)重新渲染虛擬視圖替換 它的內(nèi)容,重要的是,也會(huì)銷(xiāo)毀所有的子視圖來(lái)釋放內(nèi)存。

除了這些情景,應(yīng)用有時(shí)也會(huì)要顯式地重新渲染視圖(通常是一個(gè) ContainerView ,見(jiàn)下)。在這種情況下,應(yīng)用可以直接調(diào)用 rerender ,且 Ember 會(huì)把一項(xiàng)重渲染工作加入隊(duì)列,用相同的語(yǔ)義元素。

這個(gè)過(guò)程像是這樣:

 

 

  #p#

視圖層級(jí)

父與子

當(dāng) Ember 渲染一個(gè)模板化的視圖,它會(huì)生成一個(gè)視圖層級(jí)。讓我們假設(shè)已有一個(gè)模板 form 。

原文鏈接:

  1. {{view App.Search placeholder="Search"}} 
  2. {{#view Ember.Button}}Go!{{/view}} 

然后我們像這樣把它插入到 DOM 中:

  1. var view = Ember.View.create({ 
  2.   templateName: 'form' 
  3. }).append(); 

這會(huì)創(chuàng)建一個(gè)如下小巧的視圖等級(jí):  

 

 

 

你可以用 parentViewchildViews 屬性在視圖層級(jí)中游走。

  1. var children = view.get('childViews') // [ <App.Search><Ember.Button> ] 
  2. children.objectAt(0).get('parentView') // 視圖 

一個(gè)常見(jiàn)的 parentView 使用方法是在子視圖的實(shí)例里。

  1. App.Search = Ember.View.extend({ 
  2.   didInsertElement: function() { 
  3.     // this.get('parentView') 指向 `view` 
  4.   } 
  5. }) 

生命周期鉤子

為了容易地在視圖的生命周期的不同點(diǎn)上執(zhí)行行為,有若干你可以實(shí)現(xiàn)的鉤子。

  • willInsertElement: 這個(gè)鉤子在視圖渲染后插入 DOM 之前調(diào)用。它不提供對(duì)視圖的 element 的訪(fǎng)問(wèn)。
  • didInsertElement: 這個(gè)鉤子在視圖被插入到 DOM 后立即調(diào)用。它提供到視圖的 element 的訪(fǎng)問(wèn),且對(duì)集成到外部庫(kù)非常有用。任何顯式的 DOM 設(shè)置代碼應(yīng)限于這個(gè)鉤子。
  • willDestroyElement: 這個(gè)鉤子在元素從 DOM 移除前立即調(diào)用。這提供了銷(xiāo)毀任何與 DOM 節(jié)點(diǎn)關(guān)聯(lián)的外部狀態(tài)的機(jī)會(huì)。像 didInsertElement 一樣,它對(duì)于集成外部庫(kù)非常有用。
  • willRerender: 這個(gè)鉤子在視圖被重新渲染前立即調(diào)用。如果你想要在視圖被重新渲染前執(zhí)行一些銷(xiāo)毀操作,這會(huì)很有用。
  • becameVisible: 這個(gè)鉤子在視圖的 isVisible 或它的祖先之一的 isVisible變?yōu)檎嬷?,且關(guān)聯(lián)的元素也變?yōu)榭梢?jiàn)后調(diào)用。注意這個(gè)鉤子只在所有可見(jiàn)性由 isVisible 屬性控制的時(shí)候可靠。
  • becameHidden: 這個(gè)鉤子在視圖的 isVisible 或它的祖先之一的 isVisible變?yōu)榧僦?,且關(guān)聯(lián)的元素也變?yōu)殡[藏后調(diào)用。注意這個(gè)鉤子只在所有可見(jiàn)性由 isVisible 屬性控制的時(shí)候可靠。

應(yīng)用可以通過(guò)在視圖上定義一個(gè)與鉤子同名的方法來(lái)實(shí)現(xiàn)鉤子?;蛘?,在視圖上為鉤子 注冊(cè)一個(gè)監(jiān)聽(tīng)器也是可行的。

  1. view.on('willRerender', function() { 
  2.   // do something with view 
  3. }); 

虛擬視圖

正如上文所述,Handlebars 在視圖層級(jí)內(nèi)創(chuàng)建視圖來(lái)表現(xiàn)綁定值。每次你使用 Handlebars 表達(dá)式,無(wú)論是一個(gè)簡(jiǎn)單值還是一個(gè)諸如 {{#with}}{{#if}} 的 塊表達(dá)式,Handlebars 會(huì)創(chuàng)建一個(gè)新視圖。

因?yàn)?Ember 只把這些視圖用于內(nèi)部簿記,它們對(duì)于視圖的公共 parentViewchildViews API 是隱藏的。公共視圖層級(jí)只反射用 {{view}} 輔助標(biāo)記或通過(guò) ContainerView 創(chuàng)建的視圖(見(jiàn)下)。

例如,考慮下面的 Handlebars 模板:

  1. <h1>Joe's Lamprey Shack</h1> 
  2. {{controller.restaurantHours}} 
  3. {{#view App.FDAContactForm}} 
  4.   如果你在喬的七鰓鰻小屋用餐后不適,請(qǐng)用下面的表格向 FDA 提交申訴。 
  5.   {{#if controller.allowComplaints}} 
  6.     {{view Ember.TextArea valueBinding="controller.complaint"}} 
  7.     <button {{action submitComplaint}}>提交</button> 
  8.   {{/if}} 
  9. {{/view}} 

渲染這個(gè)模板會(huì)創(chuàng)建這樣的層級(jí):  

 

 

 幕后,Ember 跟蹤為 Handlebars 表達(dá)式創(chuàng)建的額外的虛擬視圖:  

 

 

 

 #p#

TextArea 中, parentView 會(huì)指向 FDAContactForm ,并且 FDAContactFormchildViews 會(huì)是一個(gè)只包含 TextArea 的數(shù)組。

你可以通過(guò) _parentView_childViews 來(lái)查看內(nèi)部視圖層級(jí),這會(huì)包含虛擬視 圖:

  1. var _childViews = view.get('_childViews'); 
  2. console.log(_childViews.objectAt(0).toString()); 
  3. //> <Ember._HandlebarsBoundView:ember1234> 

警告! 你不應(yīng)該在應(yīng)用代碼中依賴(lài)于這些內(nèi)部 API。它們會(huì)在任何時(shí)候更改并且 沒(méi)有任何公共合約。返回值也不能被觀(guān)察或被綁定。它可能不是 Ember 對(duì)象。如果覺(jué) 得有使用它們的需求,請(qǐng)聯(lián)系我們,這樣我們可以為你的使用需求暴露一個(gè)更好的公共 API。

底線(xiàn):這個(gè) API 就像是 XML。如果你覺(jué)得你需要用到它,那么你很可能沒(méi)有足夠理解 問(wèn)題。三思!

事件冒泡

視圖的一個(gè)任務(wù)是響應(yīng)原始用戶(hù)事件并把它們翻譯成對(duì)你應(yīng)用而言有語(yǔ)義的事件。

例如,一個(gè)刪除按鈕把原始的 click 事件翻譯成應(yīng)用特定的“把這個(gè)元素從數(shù)組中刪 除”。

為了響應(yīng)用戶(hù)事件,創(chuàng)建一個(gè)視圖的子類(lèi)來(lái)把事件實(shí)現(xiàn)為方法:

  1. App.DeleteButton = Ember.View.create({ 
  2.   click: function(event) { 
  3.     var stateManager = this.getPath('controller.stateManager'); 
  4.     var item = this.get('content'); 
  5.     stateManager.send('deleteItem', item); 
  6.   } 
  7. }); 

當(dāng)你創(chuàng)建一個(gè)新的 Ember.Application 實(shí)例,它用 jQuery 的事件委派 API 給每個(gè) 原生瀏覽器事件注冊(cè)一個(gè)事件處理器。當(dāng)用戶(hù)觸發(fā)一個(gè)事件,應(yīng)用事件分配器會(huì)找出離 事件最近的視圖并實(shí)現(xiàn)那個(gè)事件。

一個(gè)視圖通過(guò)定義與事件同名的方法來(lái)實(shí)現(xiàn)事件。當(dāng)事件名稱(chēng)由多個(gè)詞組成(如 mouseup )方法名會(huì)用 Camel 命名法把事件名作為方法名( mousUp )。

事件會(huì)在視圖層級(jí)中冒泡,直到事件到達(dá)根視圖。一個(gè)事件處理器可以用與常規(guī) jQuery 事件處理器相同的技術(shù)來(lái)停止事件傳播:

  • 在視圖中 return false
  • event.stopPropagation

例如,假設(shè)你已經(jīng)定義了如下的視圖類(lèi):

  1. App.GrandparentView = Ember.View.extend({ 
  2.   click: function() { 
  3.     console.log('Grandparent!'); 
  4.   } 
  5. }); 
  6. App.ParentView = Ember.View.extend({ 
  7.   click: function() { 
  8.     console.log('Parent!'); 
  9.     return false
  10.   } 
  11. }); 
  12. App.ChildView = Ember.View.extend({ 
  13.   click: function() { 
  14.     console.log('Child!'); 
  15.   } 
  16. }); 

這是使用它們的 Handlebars 模板。

  1. {{#view App.GrandparentView}} 
  2.   {{#view App.ParentView}} 
  3.     {{#view App.ChildView}} 
  4.       <h1>點(diǎn)擊這里!</h1> 
  5.     {{/view}} 
  6.   {{/view}} 
  7. {{/view}} 

如果你點(diǎn)擊 <h1> ,你會(huì)在瀏覽器控制臺(tái)里看見(jiàn)下面的輸出:

  1. Child! 
  2. Parent! 

你可以看出 Ember 在接受事件的最深層級(jí)視圖上調(diào)用了處理器。事件繼續(xù)上浮到 ParentView ,但不會(huì)到達(dá) GrandparentView 因?yàn)?ParentView 從它的事件處理 器中返回了 false 。

你可以使用常規(guī)事件冒泡技術(shù)來(lái)實(shí)現(xiàn)常見(jiàn)的模式。例如,你可以實(shí)現(xiàn)一個(gè)帶有 submit 方法的 FormView 。因?yàn)闉g覽器在用戶(hù)向文本域輸入回車(chē)的時(shí)候會(huì)觸發(fā) submit 事件,在表單視圖上定義一個(gè) submit 方法會(huì)“剛好完成任務(wù)”。

  1. App.FormView = Ember.View.extend({ 
  2.   tagName: "form"
  3.   submit: function(event) { 
  4.     // 會(huì)在任何用戶(hù)觸發(fā)瀏覽器的 
  5.     // `submit` 方法時(shí)被調(diào)用 
  6.   } 
  7. });
  1. {{#view App.FormView}} 
  2.   {{view Ember.TextFieldView valueBinding="controller.firstName"}} 
  3.   {{view Ember.TextFieldView valueBinding="controller.lastName"}} 
  4.   <button type="submit">確定</button> 
  5. {{/view}} 

#p#

添加新事件

Ember 內(nèi)置了如下原生瀏覽器事件的支持:

事件名

方法名

touchstart touchStart
touchmove touchMove
touchend touchEnd
touchcancel touchCancel
keydown keyDown
keyup keyUp
keypress keyPress
mousedown mouseDown
mouseup mouseUp
contextmenu contextMenu
click click
dblclick doubleClick
mousemove mouseMove

事件名

方法名

focusin focusIn
focusout focusOut
mouseenter mouseEnter
mouseleave mouseLeave
submit submit
change change
dragstart dragStart
drag drag
dragenter dragEnter
dragleave dragLeave
dragover dragOver
drop drop
dragend dragEnd

當(dāng)你創(chuàng)建一個(gè)新應(yīng)用時(shí),你可以向事件分配器添加額外的事件:

  1. App = Ember.Application.create({ 
  2.   customEvents: { 
  3.     // 添加 loadedmetadata 媒體播放器事件 
  4.     'loadedmetadata'"loadedMetadata" 
  5.   } 
  6. }); 

要使這能對(duì)自定義事件奏效,HTML5 規(guī)范必須定義事件為“bubbling”,否則 jQuery 必 須為這個(gè)事件提供一個(gè)事件委派折中方案。

模板化視圖

如同迄今你在本指導(dǎo)中所見(jiàn),你在應(yīng)用中會(huì)用的大多數(shù)視圖是依靠模板的。當(dāng)使用模板 時(shí),你不需要編寫(xiě)你的視圖層級(jí),因?yàn)槟0鍟?huì)為你創(chuàng)建它。

渲染時(shí),視圖模板可以把視圖附加到它的子視圖數(shù)組中。模板的 {{view}} 輔助標(biāo)記 內(nèi)部會(huì)調(diào)用視圖的 appendChild 方法。

調(diào)用 appendChild 會(huì)做兩件事:

  1. 把視圖添加到 childViews 數(shù)組。
  2. 立即渲染子視圖并把它添加到父視圖的渲染緩沖區(qū)。

 

 

 

 

你不應(yīng)該在視圖離開(kāi)渲染狀態(tài)后調(diào)用 appendChild 。模板渲染出“混合內(nèi)容”(包含 視圖和純文本),所以當(dāng)渲染過(guò)程完成后,父視圖不知道到底把新的子視圖插入到哪 里。

 在上例中,想象試圖把一個(gè)新視圖插入到父視圖的 childViews 數(shù)組中。它應(yīng)該立即 放在 App.MyView 的閉合標(biāo)簽 </div> 后?還是在整個(gè)視圖的閉合標(biāo)簽 </div> 后?這個(gè)答案不總是正確的。

因?yàn)檫@種含糊性,創(chuàng)建視圖層級(jí)的唯一方法就是用模板的 {{view}} 輔助標(biāo)記,它總 是把視圖插入到相對(duì)任何純文本的正確位置。

雖然這個(gè)機(jī)制對(duì)大多數(shù)情景奏效,偶爾你也會(huì)想要直接程序控制一個(gè)視圖的子視圖。在 這種情況下,你可以用 Ember.ContainerView ,它顯式地暴露了實(shí)現(xiàn)此目的的 API。

#p#

容器視圖

容器視圖不包含純文本。它們完全由子視圖(可能依靠模板)構(gòu)成。

ContainerView 暴露兩個(gè)用于修改本身內(nèi)容的公共 API:

  • 一個(gè)可寫(xiě)的 childViews 數(shù)組,你可以把 Ember.View 實(shí)例插入到其中。
  • 一個(gè) currentView 屬性,設(shè)置時(shí)會(huì)把新值插入到子視圖數(shù)組。如果存在早先的 currentView 值,它會(huì)被從 childViews 數(shù)組刪除。

這里是一個(gè)用 childViews API 創(chuàng)建新視圖的例子,由假想的 DescriptionView 開(kāi)始,并可以在任何時(shí)候用 addButton 方法添加一個(gè)新按鈕:

  1. App.ToolbarView = Ember.ContainerView.create({ 
  2.   init: function() { 
  3.     var childViews = this.get('childViews'); 
  4.     var descriptionView = App.DescriptionView.create(); 
  5.     childViews.pushObject(descriptionView); 
  6.     this.addButton(); 
  7.     return this._super(); 
  8.   }, 
  9.   addButton: function() { 
  10.     var childViews = this.get('childViews'); 
  11.     var button = Ember.ButtonView.create(); 
  12.     childViews.pushObject(button); 
  13.   } 
  14. }); 

如你在上例中所見(jiàn),我們以?xún)蓚€(gè)視圖初始化 ContainerView ,并且可以在運(yùn)行時(shí)添 加額外的視圖。存在一個(gè)方便的捷徑來(lái)設(shè)置視圖,而不用覆蓋 init 方法:

  1. App.ToolbarView = Ember.ContainerView.create({ 
  2.   childViews: ['descriptionView''buttonView'], 
  3.   descriptionView: App.DescriptionView, 
  4.   buttonView: Ember.ButtonView, 
  5.   addButton: function() { 
  6.     var childViews = this.get('childViews'); 
  7.     var button = Ember.ButtonView.create(); 
  8.     childViews.pushObject(button); 
  9.   } 
  10. }); 

如上,當(dāng)用這個(gè)速記方法時(shí),你把 childViews 指定為一個(gè)字符串?dāng)?shù)組。在初始化 時(shí),每個(gè)字符串會(huì)作為在查找視圖實(shí)例或類(lèi)的關(guān)鍵字。那個(gè)視圖會(huì)被自動(dòng)實(shí)例化,如果 必要,會(huì)加入到 childViews 數(shù)組中。  

 

          

  1. {{#if controller.isAuthenticated}} 
  2.   <h1>歡迎 {{controller.name}}</h1> 
  3. {{/if}} 
  4. {{#with controller.user}} 
  5.   <p>你有 {{notificationCount}} 條通知。</p> 
  6. {{/with}} 

在上面的模板中,當(dāng) isAuthenticated 屬性從 false 變?yōu)?true 時(shí),Ember 會(huì) 重新渲染這個(gè)塊,用原始的外部作用域作為它的上下文。

{{#with}} 輔助標(biāo)記把它的塊的上下文修改為當(dāng)前控制器的 user 屬性。當(dāng) user 屬性被修改。Ember 重新渲染塊,并用 controller.user 的新值作為它的上 下文。

#p#

視圖作用域

除了 Handlebars 上下文,Ember 中的模板也有當(dāng)前視圖的概念。無(wú)論當(dāng)前上下文是什 么, view 屬性總是引用到最近的視圖。

注意 view 屬性不會(huì)引用由 {{#if}} 之類(lèi)的塊表達(dá)式創(chuàng)建的內(nèi)部視圖。這允許你 區(qū)分 Handlebars 上下文,在 Handlebars 中和在視圖層級(jí)中的工作方式是一樣的。

因?yàn)?view 指向一個(gè) Ember.View 實(shí)例,你可以用 view.propertyName 之類(lèi)的 表達(dá)式訪(fǎng)問(wèn)視圖上的任何屬性。你可以用 view.parentView 訪(fǎng)問(wèn)視圖的父視圖。

例如,想象你有一個(gè)帶有如下屬性的視圖:

  1. App.MenuItemView = Ember.View.create({ 
  2.   templateName: 'menu_item_view'
  3.   bulletText: '*' 
  4. }); 

……和下面的模板:

  1. {{#with controller}} 
  2.   {{view.bulletText}} {{name}} 
  3. {{/with}} 

盡管 Handlebars 上下文已經(jīng)變?yōu)楫?dāng)前的控制器,你仍然可以用 view.bulletText 訪(fǎng)問(wèn)視圖的 bulletText 。

模板變量

迄今為止,我們已經(jīng)在 Handlebars 模板中邂逅了 controller 屬性。它是從哪來(lái)的呢?

Ember 中的 Handlebars 上下文可以繼承它們的父上下文中的變量。在 Ember 在當(dāng)前 上下文中查找變量之前,它首先檢查它的模板變量。當(dāng)一個(gè)視圖創(chuàng)建了一個(gè)新的 Handlebars 作用域,它們自動(dòng)繼承它們父作用域的變量。

Ember 定義了這些 viewcontroller 變量,所以當(dāng)一個(gè)表達(dá)式使用 viewcontroller 變量名,它們總是最先被找到。

如上所述,Ember 設(shè)置了 Handlebars 上下文中的 view 變量,無(wú)論何時(shí)模板中使用 了 {{#view}} 輔助標(biāo)記。起初,Ember 把 view 變量設(shè)置為正在渲染模板的視 圖。

Ember 設(shè)置了 Handlebars 上下文中的 controller 變量,無(wú)論已渲染的視圖是否存 在 controller 屬性。如果視圖沒(méi)有 controller 屬性,它從時(shí)間上最近的擁有該 屬性的視圖上繼承 controller 變量。

其它變量

Ember 中的 Handlebars 輔助標(biāo)記也會(huì)指定變量。例如, {{#with controller.person as tom}} 形式指定一個(gè) tom 變量,它的后代作用域 是可訪(fǎng)問(wèn)的。即使一個(gè)子上下文有 tom 屬性,這個(gè) tom 變量會(huì)廢除它。

這個(gè)形式的最大好處是,它允許你簡(jiǎn)寫(xiě)長(zhǎng)路徑,而不喪失對(duì)父作用域的訪(fǎng)問(wèn)權(quán)限。

{{#each}} 輔助標(biāo)記中,提供 {{#each person in people}} 形式尤其重要。 在這個(gè)形式中,后代上下文可以訪(fǎng)問(wèn) person 變量,但在模板調(diào)用 each 的地方 保留相同的作用域。

  1. {{#with controller.preferences}} 
  2.   <h1>Title</h1> 
  3.   <ul> 
  4.   {{#each person in controller.people}} 
  5.     {{! prefix here is controller.preferences.prefix }} 
  6.     <li>{{prefix}}: {{person.fullName}}</li> 
  7.   {{/each}} 
  8.   <ul> 
  9. {{/with}} 

注意這些變量繼承了 ContainerView 中的那些,即使它們不是 Handlebars 上下文 層級(jí)中的一部分。

從視圖中訪(fǎng)問(wèn)模板變量

在大多數(shù)情況下,你會(huì)需要從模板中訪(fǎng)問(wèn)這些模板變量。在一些不尋常的情景下,你會(huì) 想要在視圖的 JavaScript 代碼中訪(fǎng)問(wèn)范圍內(nèi)的變量。

你可以訪(fǎng)問(wèn)視圖的 templateVariables 屬性來(lái)達(dá)成此目的,它會(huì)返回一個(gè)包含當(dāng)視 圖渲染后存在于其作用于的變量的 JavaScript 對(duì)象。 ContainerView 也可以訪(fǎng)問(wèn) 這個(gè)屬性,它指向時(shí)間上最近的模板依賴(lài)的視圖的模板變量。

目前,你不能觀(guān)察或綁定一個(gè)包含 templateVariables 的路徑。

原文鏈接:http://emberjs.torriacg.org/guides/view_layer/#toc_

 

責(zé)任編輯:陳四芳 來(lái)源: emberjs.torriacg.org
相關(guān)推薦

2013-12-24 15:56:20

2013-12-24 11:11:27

ember.jsJavascript

2013-12-24 14:50:39

Ember.js框架

2013-12-20 14:47:23

ember.js

2013-09-10 14:01:40

WebEmber.jsAngular.js

2016-11-14 15:51:42

JavaScriptAngular.jsReact.js

2013-10-16 10:11:35

JavaScript項(xiàng)目框架

2014-03-13 11:22:00

JavaScriptJS框架

2013-12-25 09:53:22

Ember.js應(yīng)用

2013-12-25 10:08:42

ember.js異步處理

2013-12-24 13:20:28

EmberEmber.js

2013-05-30 15:16:26

javaScriptMVC模式

2021-11-29 00:17:41

JS符串轉(zhuǎn)換

2024-04-09 16:19:16

2021-12-24 15:46:23

鴻蒙HarmonyOS應(yīng)用

2010-07-19 09:31:53

SQL Server系

2015-02-09 10:43:00

JavaScript

2023-03-15 08:03:31

2010-09-27 09:54:26

Sql Server視

2010-11-16 10:42:45

Oracle創(chuàng)建視圖
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)