第34步:自定义控件

说明

在这一步中,我们将使用自定义控件扩展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. 第1步:你好世界
  2. 第2步:引导
  3. 第3步:控件
  4. 第4步:XML视图
  5. 第5步:控制器
  6. 第6步:模块
  7. 第7步:JSON模型
  8. 第8步:可翻译的文本
  9. 第9步:组件配置
  10. 第10步:应用程序描述符
  11. 第11步:页面和面板
  12. 第12步:Shell控件作为容器
  13. 第13步:外边距和内边距
  14. 第14步:自定义CSS和主题颜色
  15. 第15步:嵌套视图
  16. 第16步:对话框和片段
  17. 第17步:片段回调
  18. 第18步:图标
  19. 第19步:重用对话框
  20. 第20步:聚合绑定
  21. 第21步:数据类型
  22. 第22步:表达式绑定
  23. 第23步:自定义格式器
  24. 第24步:过滤
  25. 第25步:排序和分组
  26. 第26步:远程OData服务
  27. 第27步:模拟服务器配置
  28. 第28步:使用QUnit进行单元测试
  29. 第29步:与OPA的集成测试
  30. 第30步:调试工具
  31. 第31步:路由和导航
  32. 第32步:路由与参数
  33. 第33步:路由回溯和历史
  34. 第34步:自定义控件
  35. 第35步:响应性
  36. 第36步:设备适应
  37. 第37步:内容密度
  38. 第38步:可访问性
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值