上期咱們完成了一個簡單的主從頁面,可是頁面是靜態的,不能交互,功能也很簡單,只有一個銷售訂單的列表。 咱們今天就一氣呵成把代碼全都寫完,因爲本次的代碼量較大,因此只對重點代碼部分進行講解。 具體每一個文件和代碼就不一一貼出來了,代碼都放在github中,須要的自行下載吧。javascript
1 頁面導航
能夠先把代碼下載到本地並跑起來,這樣能夠對這個最佳實踐的程序有一個直觀的瞭解。html
頁面導航以下:
銷售訂單列表(Master) -> 銷售訂單明細(Detail) -> 行項目明細(LineItem),在每一個明細頁面均可以返回到上一層。 java
具體頁面之間的導航是如何實現的呢?
咱們從頁面的入口 index.html
開始 git
var oView = sap.ui.view({ id : "app", viewName : "ui5.tutorial.bp.view.App", type : "JS", }); //... oView.placeAt('content');
這一段代碼初始化了一個叫作App的JS view,那咱們就來看 App.view.js
github
// create app this.app = new sap.m.SplitApp(); // load the master page var master = sap.ui.xmlview("Master", "ui5.tutorial.bp.view.Master"); master.getController().nav = this.getController(); this.app.addPage(master, true); // load the empty page var empty = sap.ui.xmlview("Empty", "ui5.tutorial.bp.view.Empty"); this.app.addPage(empty, false);
App view中在 createContent
中建立了一個SplitApp,稍微說下SplitApp這個控件,當宿主是PC或者平臺的時候這個控件默認包含兩個頁面容器,而當宿主是手機的時候又能夠只包含一個頁面,因此通常主從結構的頁面能夠用這個控件。
隨後,分別建立了兩個頁面,一個是Master頁面,另外一個頁面是空白頁——做爲沒有選中任何Master頁面中的數據時的默認頁面,最後把兩個頁面都加入到了SplitApp中。
到目前爲止都是在上一篇中已經作過的,到這裏,頁面已經能夠展現了。可是咱們今天要研究的是導航,因此接着往下。
SplitApp自己帶有 to()
這個函數,能夠在已經加入其容器的頁面之間導航,可是咱們如今的功能稍稍有點複雜,當點擊Master頁面的時候,要求導航到詳細頁面, 點擊詳細頁面的行項目能夠進入到行項目的詳細頁面,對應每一個詳細頁面能夠返回至上一級頁面,同時可能還須要作一些其餘邏輯上的處理,好比判斷和綁定數據,所以咱們須要在原有的 to()
上加強一些功能並封裝給其餘的控制器使用。
web
這些功能都集中定義在 App.controller.js
這個控制器中
sql
to : function (pageId, context) { var app = this.getView().app; // load page on demand var master = ("Master" === pageId); if (app.getPage(pageId, master) === null) { var page = sap.ui.view({ id : pageId, viewName : "ui5.tutorial.bp.view." + pageId, type : "XML" }); page.getController().nav = this; app.addPage(page, master); jQuery.sap.log.info("app controller > loaded page: " + pageId); } // show the page app.to(pageId); // set data context on the page if (context) { var page = app.getPage(pageId); page.setBindingContext(context); } }, /** * Navigates back to a previous page * @param {string} pageId The id of the next page */ back : function (pageId) { this.getView().app.backToPage(pageId); }
從新封裝後的 to()
有兩個參數,一個是頁面id,另外一個是上下文,當傳入的頁面不存在的時候,系統會首先初始化這個頁面並加入到當前的SplitApp中,隨後直接調用SplitApp的 to()
完成導航動做。 最後,若是上下文不爲空的話,把這個上下文綁定到新的頁面中,這對於一個新頁面來講是很是有意義的。
back則直接複用SplitApp的 backToPage()
函數。 json
接下來咱們再看其餘的頁面如何複用定義在這裏的導航函數的。
咱們回過頭來再看 App.view.js
, 其中在初始化Master頁面以後,有這麼一條語句
master.getController().nav = this.getController();
這條語句把當前的控制器賦給Master頁面的控制器的nav,這個nav只是用來存放App控制器的一個key,叫什麼名字都行,這樣在Master頁面中就能夠經過nav來調用App控制器的全部函數了。 瀏覽器
一樣的,再回到 App.controller.js
中,看到這條語句 page.getController().nav = this;
也是相似的做用。 bash
在首頁面剛剛初始化時,Detail頁面是沒有加載的,當點擊Master頁面中的某個SalesOrder的時候,Master頁面中的 handleListItemPress
被調用:
handleListItemPress : function (evt) { var context = evt.getSource().getBindingContext(); this.nav.to("Detail", context); }
首先得到被點擊的item上綁定的上下文信息(數據),而後調用App的 to()
方法並傳入 Detail
告訴頁面要加載Detail這個頁面,同時把上下文數據傳遞過去, 接下來又回到了 to()
, Detail頁面被初始化,綁定上下文數據,加載到SplitApp中,而後導航成爲當前顯示頁面。
以上,就完成了這個App的導航,能夠看到有些繁瑣,而且須要顯式的初始化子頁面的時候得到並共享App的控制器,另外,頁面之間的跳轉不會修改URL,沒法將某個中間頁面存爲bookmark,因此這種方式並非UI5所推薦的, 可是做爲初學者瞭解UI5的頁面導航機制仍是很是的直觀,另外對於簡單的應用來講,若是頁面較少也何嘗不能夠考慮。
做爲稍大型的web應用,UI5在早期的版本中推薦使用EventBus經過Event的傳遞來實現複雜的頁面導航,從1.6開始引入了新的導航機制,就是Routing,能夠將頁面之間的導航關係定義在component中,在最新的1.30版本中,導航定義則能夠直接寫在App的說明文件 manifest.jso
中。
導航就介紹到這裏,Component和Routing是一個比較複雜可是很是強大的工具,咱們能夠在後續接着探討。
2 數據綁定
在咱們的代碼中,數據綁定也是作了簡化處理,都直接寫在 index.html
中了。
一共綁定了三個模型:
- 業務數據模型:
由於咱們使用的是離線的json格式數據,因此能夠直接把相對路徑傳遞給sap.ui.model.json.JSONModel
來初始化這個模型,並綁定到App這個根視圖上。var oModel = new sap.ui.model.json.JSONModel("model/mock.json"); oView.setModel(oModel);
隨後在這個視圖及其子視圖中,均可以直接經過相似
{SoId}
這種語法格式來使用這個模型的數據字段,須要注意的是,若是須要綁定的字段是這個模型的根節點,須要在前面加一個/
,譬如在Master視圖中綁定到列表的aggregate字段items
,是這樣的語法格式:items="{/SalesOrderCollection}" 。 - 多語言模型:
UI5中使用了i18n
機制來處理多語言問題。i18n是 internationalization的簡稱,在首位兩個字母之間有18個字母……具體如何使用很是的簡單,
首先建立一個資源文件messageBundle.properties
,這裏咱們在根目錄建立了一個i18n
目錄,在這裏目錄中集中存放相關的i18n文件。
在這個資源文件裏咱們定義以下:MasterTitle=Sales Orders
DetailTitle=Sales Order
StatusTextN=New
StatusTextP=In Process
ApproveButtonText=Approve
…左邊的是KEY,右邊的是對應的語言描述,若是咱們須要定義一箇中文的語言文件,那麼只須要拷貝這個文件並重命名爲
messageBundle_zh-CN.properties
,並將對應的描述改成中文以下:
MasterTitle=銷售訂單列表 DetailTitle=銷售訂單 StatusTextN=新建 StatusTextP=處理中 ApproveButtonText=批准 …
系統會根據用戶設定的瀏覽器語言順序依次查找對應的語言資源文件,若是都找不到的話,就會找默認的
messageBundle.properties
。定義好了資源文件,咱們接下來就在
index.html
中經過sap.ui.model.resource.ResourceModel
來初始化這個資源模型,接着就能夠把它綁定到視圖或者控件中使用了。
怎麼使用呢?在視圖中經過相似{i18n>MasterTiel}
這種語法格式來綁定到對應的空間的文本項上,實際使用中須要用引號把這個串包含進去。這個串中前面的i18n>
指的是引用綁定到本視圖的叫作i18n的模型,這裏的i18n是綁定時起的名字,oView.setModel(i18nModel, "i18n");
能夠是任意符合格式的字符串。
- 設備模型 設備模型經過查詢jQuery的device來獲悉宿主是否手機,並設定相應的不一樣顯示選項,而後將結果存爲Json格式並初始化爲一個JSON模型,最後綁定到模型中。
3 工具方法
大多數狀況下,咱們能夠直接把業務數據直接綁定到控件中顯示,可是在一些狀況下,咱們可能須要對其中的一些格式作一些調整,或者根據一些字段作一些簡單的邏輯處理, 這個時候,咱們就須要用到大多數控件中的某些屬性的 formatter
方法。
<ObjectStatus text="{ path: 'LifecycleStatus', formatter: 'ui5.tutorial.bp.util.Formatter.statusText' }" state="{ path: 'LifecycleStatus', formatter: 'ui5.tutorial.bp.util.Formatter.statusState' }" />
上面這個例子中,咱們來看 text
屬性,若是咱們但願直接把業務數據綁定到text中,咱們這樣定義 text="{LifecycleStatus}",可是咱們知道這個字段多是後臺定義的技術字段,咱們須要把它轉化的比較有業務意義。 因此這個時候,咱們就須要用到 formatter
了,首先定義 path
告之須要綁定的字段,這裏不須要用大括號,隨後給 formatter
賦予一個處理方法,這個方法能夠定義在任何地方,咱們這裏是在util下單獨定義了一個 Fomatter.js
來集中處理這類需求。
來看 Formatter.js
,咱們就看 statusText
這個方法:
statusText : function (value) { var bundle = this.getModel("i18n").getResourceBundle(); return bundle.getText("StatusText" + value, "?"); },
path
中綁定的字段對應的值會做爲參數傳入,而後用這個值結合StatusText生成一個KEY,並在 i18n
中取出相應的描述。
4 總結
基本上這個最佳實踐應用已經被剖析完成了,經過這樣一個最佳實踐 Best Practice
的練習,咱們學習到了通常的UI5應用的總體結構以及大多數重要控件的使用方法。