Fiori-MVC

很有必要把MVC单独拿出来认真的讨论一次。

MVC的好处我们就不多谈了,百度很多,但在SAPGUI中是如何做的呢?

简单来说

  • Model: 专门管理application中的数据
  • View: 定义和渲染UI
  • Controller: 对View Event进行反应和在用户对View和Model修改进行交互时进行反应
关系

View和Controller的关系一般是1:1,但也允许多个Controller控件一个View,他们叫们应用控件器。当然View也可没有对应的Controller。一般1个View可以有或继承一个SAPUI5 Model。

Model

数据绑定

Model One-way Two-way One-time
Resource model X
JSON model X X X
XML model X X X
OData model X X X
Model Default binding mode
Resource model One-time
JSON model Two-way
XML model Two-way
OData model One-way

一般来说, MVC中的Model主要是用来hold数据并且提供方法去访问和更新数据库中的数据。
SAPUI5提供了以下几种预定义的Model:

  • OData Model
    OData Model支持多种绑定模式:双向(默认)绑定,意向绑定,一定性绑定。但需要注意的是双向绑定只支持属性值,不能对aggregations。OData Model现在有两个版本:OData V2和OData V4。
  • JSON Model
    JSON Model可以用来绑定控件到序列化的JavaScript对象数据上,它是客户端上的一种Model,所以当数据量不是在太大,完全可以保存在客户端时就可以选择使用JSON Model。它支持双向(默认)绑定、单向绑定和一次性绑定。
  • XML Model
    使用上基本上和JSON Model一致的,适用少量数据。
  • Resource Model
    这个主要是用来处理多语言的数据,比如保存多个语言的提示消息和文本信息,因为它只处理静态文本,所以这个只支持一次性绑定。

就是下面这张图,除了OData Model,其它三种都是做在客户端的,不需要访问server就可以使用。OData Model是服务器端的Model,需要UI通过请求从server取数。除这些之外还可以开发自定义的Model。

OData V2 Model

OData Model 是服务器端的Model,意味着数据集只在server端有效,数据的操作,比如排序和过滤都是在server来完成,client只有发送请求得到数据。

发往后端数据请求的发起可以由list binding(ODataListBinding),element binding(ODataContextBinding)和 OData Model的CRUD function来触发。 Property binding(ODataPropertyBinding)不能触发请求。

OData Model现在支持OData version 2.0,相比1.0以下面几个特性:

  1. 默认是JSON 格式
  2. 双向绑定只支持Property change,还不支持aggregations
  3. 默认为单向绑定
  4. 可以在client端进行排序和数据过滤
  5. 所有的请求都支持Bundle
  6. 所有的数据可以被cached in Model
  7. 支持message handling
创建Model实例

1 个Model实例只能负责 1 个OData服务,如果要访问多个服务,就必须要创建多个实例。创建OData Model实例必须有的一个参数就是服务的URL。第一个参数

1
2
var oModel = new sap.ui.model.odata.v2.ODataModel("http://services.odata.org/Northwind/Northwind.svc/");
var oModel = new sap.ui.model.odata.v2.ODataModel({serviceUrl: "http://services.odata.org/Northwind/Northwind.svc"});

每当创建一个ODataModel时就会发送一个metadata的服务请求,如果多个ODataModel使用同一个服务的话,仅第一个实例触发metadata请求。

1
http://services.odata.org/Northwind/Northwind.svc/$metadata

实例的getServiceMetadata()方法可以查看JSON格式的metadata

1
var oMetadata = oModel.getServiceMetadata();

可以增加附加参数来配置OData services,SAPUI5会根据相应的绑定方式自动设定一些参数,ODataModel现在支持$select$expend,可以有不同的方式来增加这些附件参数:

  • 直接在URL后加:

    1
    var oModel = new sap.ui.model.odata.v2.ODataModel("http://myserver/MyService.svc/?myParam=value&myParam2=value");
  • 用mparameters map传递给URL(也可以只传入一个):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var oModel = new sap.ui.model.odata.v2.ODataModel({
    serviceUrl: "http://services.odata.org/Northwind/Northwind.svc",
    serviceUrlParams: {
    myParam: "value1",
    myParam2: "value2"
    },
    metadataUrlParams: {
    myParam: "value1",
    myParam2: "value2"
    }
    });

除了serviceUrlserviceUrlParams之外,还有headers可以指定HTTP的headers:

1
2
3
4
5
6
var oModel = new sap.ui.model.odata.v2.ODataModel({
headers: {
"myHeader1" : "value1",
"myHeader2" : "value2"
}
});

1
oModel.setHeaders({"myHeader1" : "value1", "myHeader2" : "value2"});

注意当指定HTTP header参数时原有的参数会被覆盖,有些内部的参数是改不了的,如:

1
2
3
4
5
"accept"
"accept-language"
"maxdataserviceversion"
"dataserviceversion"
"x-csrf-token"

寻址Entities:绑定语法

ODataModel的路径绑定语法和相对于在OData中用于访问特定Entity或EntitySet服务URL的URL路径相匹配。
根据服务metadata中定义的OData服务的结构,您可以访问OData模型提供的数据。URL参数,例如过滤器,是不能添加到绑定路径中的。绑定路径可以是绝对路径,也可以是相对路径。绝对绑定路径立即被解析。 相对路径只有在能被转换成绝对路径的时候才可用。例如,如果一个Property绑定了一个相对路径而它的父控件绑定了一个绝对路径,那这个相对路径就可以解析成一个绝对路径。

The following binding samples within the ODataModel are taken from the Northwind demo service.

绝对路径:

1
2
"/Customers"
"/Customers('ALFKI')/Address"

相对路径如果有一个context的话就可能被解析成绝对路径,比如有一个绝对路径/Customer('ALFKI'),那么下面的相对路径

1
2
3
"CompanyName"
"Address"
"Orders"

就可以被转换成下面的绝对路径:

1
2
3
"/Customer('ALFKI')/CompanyName"
"/Customer('ALFKI')/Address"
"/Customer('ALFKI')/Orders"

Navigation properties可以被用来视别一个Entity或是多个Entities

1
2
"/Customers('ALFKI')/Orders"
"/Products(1)/Supplier"

访问OData Model的数据

从一个OData服务请求的数据会被cathed到OData Model,可以使用getData()getProperty()方法访问,分别返回ODataModel的Entity对象和值。这些方法并不是从后台Server访问数据,所以你只能从那些请求过数据的Model中取。

1
2
oModel.getData("/Customer('ALFKI')");
oModel.getProperty("/Customer('ALFKI')/Address");

另外,这些方法只能访问单一的Entity和Property,如果要访问EntitySet的数据,需要通过List binding,这样就可以得到这些Entities的Binding context。

不要手工去修改Model内的对象或数据,一定要使用SAP提供的API来操作,或是用双向绑定的控件来修改数据。

创建Entities
为指定的EntitySet创建Entity,用createEntry()方法,方法返回一个context对象指定新创建的Entity,application可以通过双向绑定的方式对它的值进行修改,保存数据需要用到submitChanges(),回滚的话用deleteCreatedEntry()

application可以选择在创建的对象中包含哪些属性,并且可以为这些属性传递自己的默认值,默认情况下,所有的属性值都是空的,未定义的。

EntitySet和传递的属性值必须在OData服务的metadata中存在

1
2
3
4
5
6
7
8
// create an entry of the Products collection with the specified properties and values
var oContext = oModel.createEntry("/Products", { properties: { ID:99, Name:"Product", Description:"new Product", ReleaseDate:new Date(), Price:"10.1", Rating:1} });
// binding against this entity
oForm.setBindingContext(oContext);
// submit the changes (creates entity at the backend)
oModel.submitChanges({success: mySuccessHandler, error: myErrorHandler});
// delete the created entity
oModel.deleteCreatedEntry(oContext);

如果已经提交了创建的Entity,那么Context将通过创建请求返回的路径进行更新,并将新数据导入到模型中,这样Context仍然有效,并指向新创建的Entity。

CRUD Operations
OData service是可以手工进行CRUD(Create,Read,Update,Delete)的,如果手工操作有返回值 ,数据会被Import到这个ODataModel的Cathe中,所有操作都需要一个强制性的sPath参数以及一个可选的mParameters map,Create和Update操作还需要另一个强制的参数oData来传递要创建或修改的对象,每个操作返回一个包含一个函数中止的对象,这个对象可以用来中止请求。如果请求被中止,将调用错误处理程序。这将确保为每个请求执行成功或错误处理程序。还可以传递附加的头数据、URL参数或eTag。

  • Creating entities
    create函数触发一个OData服务的POST请求,该服务是在创建OData模型时指定的。应用程序必须指定EntitySet,其中将创建新的Entity和Entity数据。

    1
    2
    3
    4
    5
    var oData = {
    ProductId: 999,
    ProductName: "myProduct"
    }
    oModel.create("/Products", oData, {success: mySuccessHandler, error: myErrorHandler});
  • Reading entities
    read函数触发一个GET请求到指定的路径。在创建OData模型时,将从OData服务中检索路径。检索到的数据在成功回调处理程序函数中返回。

    1
    oModel.read("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
  • Updating entities
    update函数会触发一个对OData服务的put/merge请求,该服务是在OData模型创建时指定的。在成功请求更新模型中的绑定之后,刷新会自动触发。

    1
    2
    3
    4
    5
    var oData = {
    ProductId: 999,
    ProductName: "myProductUpdated"
    }
    oModel.update("/Products(999)", oData, {success: mySuccessHandler, error: myErrorHandler});
  • Deleting entities
    remove函数触发对OData服务的删除请求,该服务是在创建OData模型时指定的。应用程序必须指定要删除的条目的路径。

    1
    oModel.remove("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
  • Refresh after change
    该模型提供了一种机制来自动刷新依赖于发生更改的Entity的绑定。如果您执行一个创建、更新或删除功能,该模型将识别绑定并触发这些绑定的刷新。如果模型以批处理模式运行,则刷新请求与相同批处理请求中的更改捆绑在一起。您可以通过调用setRefreshAfterChange(false)禁用自动刷新。如果自动刷新被禁用,应用程序必须处理刷新各自的绑定。

    1
    oModel.setRefreshAfterChange(false);

并发控制、ETags
OData使用HTTP ETags来实现优化并发控制。必须配置服务才能提供这些服务。每个CRUD请求的参数映射中都可以传递ETag。如果没有传递ETag,则使用缓存实体的ETag,前提是已经加载了Cach。
XSRF Token
为了解决跨站点请求的伪造,OData服务可能需要XSRF令牌来满足客户端应用程序的更改请求。在这种情况下,客户端必须从服务器获取一个令牌,并将每个更改请求发送到服务器。在读取元数据时,OData模型会获取XSRF标记,然后自动将其发送给每个写请求头。如果令牌不再有效,则可以通过调用OData模型中的refreshSecurityToken函数来获取新的令牌。令牌以对服务文档的请求来获取。为了确保获得有效的令牌,请确保服务文档没有被缓存。
Refreshing the Model
刷新函数刷新了一个OData模型中的所有数据。每个绑定从服务器重新加载它的数据。对于列表或元素绑定,将触发对后端的一个新请求。如果XSRF令牌不再有效,则必须使用对服务文档的read请求再次获取它。通过手动CRUD请求导入的数据不会自动重新加载。
Batch Processing
v2.ODataModel 支持两种方式的batch处理:

  • 默认:一个线程中的所有请求都被收集并打包在一个batch批处理请求中,这意味着在当前调用堆栈完成后立即将请求发送到超时。这包括所有的手动CRUD请求以及绑定所触发的请求。
  • 延迟:

使用MVC,sapui5如何确定view和controller的文件的名称和位置呢? 有以下三种方法来声明文件的位置:

  1. sap.ui.localResources()
  2. jQuery.sap.registerModulePath()
  3. bootstrap声明: data-sap-ui-resourceroots=’{“name”: “”}’

sap.ui.localResources()

sap.ui.localResources()语法:

1
sap.ui.localResources(sModuleNamePrefix);

将sModuleNamePrefix注册为html文件(比如index.html作为当前文件夹)下sModuleNamePrefix子文件夹。如果sModuleNamePrefix中含有点号,所有的点被替换成/。比如,view name为master,如果sap.ui.localResources("myapp.viewsforbutton");,则sapui在./myapp/viewforbutton文件夹下查找view文件的代码。文件的扩展名由view的类型决定。

jQuery.sap.registerModulePath()

jQuery.sap.registerModulePath()的语法:

1
jQuery.sap.registerModulePath(sModuleNamePrefix, sURL);

把sModuleNamePrefix注册为sURL,比如:jQuery.sap.registerModulePath("mainview", "./mainview/");把mainview注册为当前文件夹下的mainview文件夹。
这种方法能实现sap.ul.localResources()相同的功能,但更加灵活。

bootstrap申明resourcesroots

下面的声明实现上述同样功能。

1
2
3
4
5
6
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-resourceroots='{"mainview" : "./mainview/"}'>
</script>

Model的绑定

一个Model可以被set到core,component controller,view,controls(Aggregation),controls(element),controls(property)不同的level。所有controls和views都会集成父级的model。

注意: Fiori App不要set model到UI core,应该set到对应的view或是component controller。

控件和model需要通过路径绑定进行显示,有以下几种绑定类型:

  1. Property binding
  2. Element binding
  3. Aggregation binding

Aggregation and Expression Binding

Aggregation Binding用于绑定一个多行数据的数据集到一个UI控件上,比如tables,lists或pulldown。语法:bindAggregation(sPath,oTemplate)

Expression Binding可以用表达式来替代custom formatter functions。只要用在一些细微的控制。比如判断字段的visible属性,下面是一个对比的例子:

参考

http://blog.csdn.net/stone0823/article/details/53955928

Jim Guo wechat
ex. subscribe to my blog by scanning my public wechat account