说明
在这一步中,我们将使用自定义控件扩展SAPUI5的功能。我们希望对详细页面上显示的产品进行评级,因此我们使用SAPUI5扩展机制创建了多个标准控件的组合,并添加了一些粘合代码,使它们能够很好地一起工作。这样,我们就可以在整个应用程序中重用控件,并将所有相关功能保存在一个模块中。
预览
将自定义产品评级控件添加到详细页面
代码
您可以在演练-步骤34查看和下载所有文件。
webapp/control/ProductRating.js (New)
sap.ui.define([
"sap/ui/core/Control"
], function (Control) {
"use strict";
return Control.extend("sap.ui.demo.walkthrough.control.ProductRating", {
metadata : {
},
init : function () {
},
renderer : function (oRM, oControl) {
}
});
});
我们创建了一个新的文件夹control和一个ProductRating.js文件,该文件将保存我们的新控件。与我们的控制器和视图一样,自定义控件从SAPUI5基对象继承了公共控件功能,对于控件,这是通过扩展基类sap.ui.core.Control实现的。
自定义控件是可以在应用程序中很容易创建的小型重用组件。由于它们的特性,它们有时也被称为“notepad”或“on the fly”控件。自定义控件是一个JavaScript对象,它有两个特殊的部分(metadata和renderer)和许多实现控件功能的方法。
metadata部分定义数据结构,从而定义控件的API。利用控件的属性、事件和聚合的元信息,SAPUI5自动创建setter和getter方法以及其他可在应用程序中调用的方便函数。
渲染器定义了HTML结构,当控件在视图中实例化时,该结构将被添加到应用程序的DOM树中。它通常最初由SAPUI5的核心调用,并且每当控件的属性被更改时都会调用它。渲染函数的参数oRM是SAPUI5渲染管理器,可用于将字符串和控件属性写入HTML页面。
init方法是一个特殊的函数,每当控件实例化时,SAPUI5核心都会调用它。它可用于设置控件并准备显示其内容。
注意
控件总是扩展sap.ui.core.Control并渲染它们自己。如果希望重用SAPUI5的生命周期特性,包括未呈现对象的数据绑定,还可以直接扩展sap.ui.core.Element或sap.ui.base.ManagedObject。请参考API参考以了解更多关于控件继承层次结构的信息。
webapp/control/ProductRating.js
sap.ui.define([
"sap/ui/core/Control",
"sap/m/RatingIndicator",
"sap/m/Label",
"sap/m/Button"
], function (Control, RatingIndicator, Label, Button) {
"use strict";
return Control.extend("sap.ui.demo.walkthrough.control.ProductRating", {
metadata : {
properties : {
value: {type : "float", defaultValue : 0}
},
aggregations : {
_rating : {type : "sap.m.RatingIndicator", multiple: false, visibility : "hidden"},
_label : {type : "sap.m.Label", multiple: false, visibility : "hidden"},
_button : {type : "sap.m.Button", multiple: false, visibility : "hidden"}
},
events : {
change : {
parameters : {
value : {type : "int"}
}
}
}
},
init : function () {
this.setAggregation("_rating", new RatingIndicator({
value: this.getValue(),
iconSize: "2rem",
visualMode: "Half",
liveChange: this._onRate.bind(this)
}));
this.setAggregation("_label", new Label({
text: "{i18n>productRatingLabelInitial}"
}).addStyleClass("sapUiSmallMargin"));
this.setAggregation("_button", new Button({
text: "{i18n>productRatingButton}",
press: this._onSubmit.bind(this)
}).addStyleClass("sapUiTinyMarginTopBottom"));
},
setValue: function (fValue) {
this.setProperty("value", fValue, true);
this.getAggregation("_rating").setValue(fValue);
},
reset: function () {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
this.setValue(0);
this.getAggregation("_label").setDesign("Standard");
this.getAggregation("_rating").setEnabled(true);
this.getAggregation("_label").setText(oResourceBundle.getText("productRatingLabelInitial"));
this.getAggregation("_button").setEnabled(true);
},
_onRate : function (oEvent) {
var oRessourceBundle = this.getModel("i18n").getResourceBundle();
var fValue = oEvent.getParameter("value");
this.setProperty("value", fValue, true);
this.getAggregation("_label").setText(oRessourceBundle.getText("productRatingLabelIndicator", [fValue, oEvent.getSource().getMaxValue()]));
this.getAggregation("_label").setDesign("Bold");
},
_onSubmit : function (oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
this.getAggregation("_rating").setEnabled(false);
this.getAggregation("_label").setText(oResourceBundle.getText("productRatingLabelFinal"));
this.getAggregation("_button").setEnabled(false);
this.fireEvent("change", {
value: this.getValue()
});
},
renderer : function (oRm, oControl) {
oRm.openStart("div", oControl);
oRm.class("myAppDemoWTProductRating");
oRm.openEnd();
oRm.renderControl(oControl.getAggregation("_rating"));
oRm.renderControl(oControl.getAggregation("_label"));
oRm.renderControl(oControl.getAggregation("_button"));
oRm.close("div");
}
});
});
现在,我们用所需的自定义功能来增强新的自定义控件。在我们的例子中,我们想要创建一个交互式的产品评级,所以我们定义了一个值,并使用了三个内部控件,这些控件会自动更新显示它们。RatingIndicator控件用于收集用户对产品的输入,标签显示进一步的信息,按钮将评级提交给应用程序来存储它。
因此,在metadata部分,我们定义了几个在实现中使用的属性:
Properties
- Value
我们定义一个控件属性值,该值将保存用户在评级中选择的值。此属性的Getter和setter函数将自动创建,如果愿意,我们还可以将其绑定到XML视图中的数据模型字段。Aggregations
如第一段所述,我们需要三个内部控制来实现我们的评级功能。因此,我们通过将visibility 属性设置为hidden来创建三个“hidden aggregations”。通过这种方式,我们可以在内部控件中使用在视图上设置的模型,SAPUI5将负责生命周期管理,并在不再需要控件时销毁它们。聚合也可以用来保存控件数组,但我们只想在每个聚合中有一个控件,所以我们需要通过将属性multiple设置为false来调整基数。
- _rating:一个用于用户输入的sap.m.RatingIndicator控件
- _label:用于显示附加信息的sap.m.Label控件
- _button:一个来提交评级sap.m.t button控件
注意
您可以为控件定义聚合和关联。区别在于父控件和相关控件之间的关系:
- aggregation是一种强关系,它还管理相关控件的生命周期,例如,当父控件被销毁时,相关控件也会被销毁。此外,一个控件只能分配给一个单独的聚合,如果它被分配给第二个聚合,它将自动从前面的聚合中删除。
- association是一种不管理生命周期的弱关系,可以定义多次。为了有清晰的区别,关联只存储ID,而聚合存储对控件的直接引用。在本例中,我们没有指定关联,因为我们希望由父节点管理内部控制。
Events
- Change
我们指定一个更改事件,该事件将在提交评级时触发控件。它包含当前值作为事件参数。
应用程序可以注册到这个事件,并以类似于“常规”SAPUI5控件的方式处理结果,实际上,这些控件的构建方式类似于自定义控件。
每当实例化控件的新实例时,SAPUI5就会自动调用init函数,在这个函数中,我们设置内部控件。我们实例化这三个控件,并通过调用继承自sap.ui.core.Control的框架方法setAggregation将它们存储在内部聚合中。我们传递上面指定的内部聚合的名称和新的控制实例。我们指定一些控件属性,使我们的自定义控件看起来更好,并注册一个liveChange事件到评级和一个press事件到按钮。标签和按钮的初始文本引用自我们的i18n模型。
现在让我们先忽略其他内部helper函数和事件处理程序,定义我们的渲染器。在SAPUI5呈现管理器和作为引用传递的控件实例的帮助下,我们现在可以呈现控件的HTML结构。我们将外层< div >标签的开头呈现为<div,并调用辅助方法writeControlData来呈现div标记内控件的ID和其他基本属性。接下来,我们添加一个自定义CSS类,以便稍后在CSS文件中为自定义控件定义样式规则。这个CSS类和其他已经添加到视图中的CSS类然后通过调用呈现器实例上的writeClasses来呈现。然后我们关闭周围的div标签,并通过将内部聚合的内容传递给呈现管理器renderControl函数来呈现三个内部控件。这将调用控件的渲染器,并将它们的HTML添加到页面中。最后,关闭周围的< div >标签。
setValue是一个setter的overridden。SAPUI5将生成一个setter,在控制器中调用或在XML视图中定义时更新属性值,但是我们还需要更新隐藏聚合中的内部评级控制,以正确反映状态。此外,通过调用setProperty方法以true作为第三个参数更新控件属性,我们可以跳过SAPUI5的重新呈现,这通常在控件上的属性更改时触发。
现在,我们为内部评级控制定义事件处理程序。每次用户更改评级时都会调用它。评级控制的当前值可以从sap.m.RatingIndicator控制的事件参数值读取。使用这个值,我们调用overridden的setter来更新控件状态,然后我们更新评级旁边的label,以显示用户当前选择的值,并显示最大值。带有占位符值的字符串从自动分配给控件的i18n模型中读取。
接下来,我们有提交评级的评级按钮的按下处理程序。我们假设对产品进行评级是一次性的操作,首先禁用评级和按钮,这样用户就不允许提交另一个评级。我们还更新标签以显示“Thank you for your rating!”消息,然后我们触发控件的更改事件,并将当前值作为参数传递进来,以便侦听该事件的应用程序可以对评级交互做出反应。
我们定义了reset方法,以便能够将UI上的控件状态恢复到初始状态,以便用户可以再次提交评级。
webapp/view/Detail.view.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.Detail"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
xmlns:wt="sap.ui.demo.walkthrough.control">
<Page
title="{i18n>detailPageTitle}"
showNavButton="true"
navButtonPress=".onNavBack">
<ObjectHeader
intro="{invoice>ShipperName}"
title="{invoice>ProductName}"/>
<wt:ProductRating id="rating" class="sapUiSmallMarginBeginEnd" change=".onRatingChange"/>
</Page>
</mvc:View>
在详细视图上定义了一个新的命名空间wt,这样我们就可以在视图中轻松地引用自定义控件。然后,我们将ProductRating控件的一个实例添加到详细信息页面,并为更改事件注册一个事件处理程序。为了有一个正确的布局,我们还添加了一个边距样式类。
webapp/controller/Detail.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/routing/History",
"sap/m/MessageToast"
], function (Controller, History, MessageToast) {
"use strict";
return Controller.extend("sap.ui.demo.walkthrough.controller.Detail", {
…
_onObjectMatched: function (oEvent) {
this.byId("rating").reset();
this.getView().bindElement({
path: "/" + window.decodeURIComponent(oEvent.getParameter("arguments").invoicePath),
model: "invoice"
});
},
onNavBack: function () {
var oHistory = History.getInstance();
var sPreviousHash = oHistory.getPreviousHash();
if (sPreviousHash !== undefined) {
window.history.go(-1);
} else {
var oRouter = this.getOwnerComponent().getRouter();
oRouter.navTo("overview", {}, true);
}
},
onRatingChange: function (oEvent) {
var fValue = oEvent.getParameter("value");
var oResourceBundle = this.getView().getModel("i18n").getResourceBundle();
MessageToast.show(oResourceBundle.getText("ratingConfirmation", [fValue]));
}
});
});
在Detail控制器中,我们将依赖加载到sap.m.MessageToast,因为我们将简单地显示一条消息,而不是将评级发送到后端,以保持示例简单。事件处理程序onRatingChange读取在评级提交时触发的自定义更改事件的值。然后在MessageToast控件中显示一个带有值的确认消息。
在onObjectMatched私有方法中,我们调用reset方法,以便在显示另一个不同行项目的详细视图时提交评级。
webapp/css/style.css
.myAppDemoWTmyCustomButton.sapMBtn {
margin-right: 0.125rem;
}
.myAppDemoWTmyCustomText {
font-weight: bold;
}
/* ProductRating */
.myAppDemoWTProductRating {
padding: 0.75rem;
}
.myAppDemoWTProductRating .sapMRI {
vertical-align: initial;
}
为了布局控件,我们在根类中添加了一些padding,以便在三个内部控件周围留出一些空间,并且我们重写了RatingIndicator控件的对齐方式,以便它与标签和按钮对齐在一行中。
我们也可以在渲染器中使用更多的HTML来实现这一点,但这是最简单的方法,它只会应用在我们的自定义控件中。但是,请注意,自定义控件在您的应用程序中,可能需要在SAPUI5未来版本的内部控件更改时进行调整。
webapp/i18n/i18n.properties
…
# Detail Page
detailPageTitle=Walkthrough - Details
ratingConfirmation=You have rated this product with {0} stars
# Product Rating
productRatingLabelInitial=Please rate this product
productRatingLabelIndicator=Your rating: {0} out of {1}
productRatingLabelFinal=Thank you for your rating!
productRatingButton=Rate
资源包扩展了confirmation message和我们在自定义控件中引用的字符串。我们现在可以在详细页面上用我们全新的控件来评价产品。
约定
- 将自定义控件放在应用程序的control文件夹中。
章节
- 第1步:你好世界
- 第2步:引导
- 第3步:控件
- 第4步:XML视图
- 第5步:控制器
- 第6步:模块
- 第7步:JSON模型
- 第8步:可翻译的文本
- 第9步:组件配置
- 第10步:应用程序描述符
- 第11步:页面和面板
- 第12步:Shell控件作为容器
- 第13步:外边距和内边距
- 第14步:自定义CSS和主题颜色
- 第15步:嵌套视图
- 第16步:对话框和片段
- 第17步:片段回调
- 第18步:图标
- 第19步:重用对话框
- 第20步:聚合绑定
- 第21步:数据类型
- 第22步:表达式绑定
- 第23步:自定义格式器
- 第24步:过滤
- 第25步:排序和分组
- 第26步:远程OData服务
- 第27步:模拟服务器配置
- 第28步:使用QUnit进行单元测试
- 第29步:与OPA的集成测试
- 第30步:调试工具
- 第31步:路由和导航
- 第32步:路由与参数
- 第33步:路由回溯和历史
- 第34步:自定义控件
- 第35步:响应性
- 第36步:设备适应
- 第37步:内容密度
- 第38步:可访问性