OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(下)

 

上期咱们完成了一个简单的主从页面,可是页面是静态的,不能交互,功能也很简单,只有一个销售订单的列表。 咱们今天就一气呵成把代码全都写完,因为本次的代码量较大,因此只对重点代码部分进行讲解。 具体每一个文件和代码就不一一贴出来了,代码都放在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.jsgithub

// 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应用的总体结构以及大多数重要控件的使用方法。

相关文章
相关标签/搜索