在这一步中,我们将使用自定义控件扩展SAPUI5的功能。我们希望对详细页面上显示的产品进行评级,因此我们使用SAPUI5扩展机制创建了多个标准控件的组合,并添加了一些粘合代码以使它们能够很好地一起工作。这样,我们可以在整个应用程序中重用控件,并将所有相关功能保存在一个模块中。
Preview
A custom product rating control is added to the detail page
Coding
You can view and download all files at Walkthrough - Step 34.
webapp/control/ProductRating.js (New)
sap.ui.define([ "sap/ui/core/Control" ],function(Control){ "use strict"; returnControl.extend("sap.ui.demo.walkthrough.control.ProductRating",{ metadata :{ }, init :function(){ }, renderer :function(oRM, oControl){ } }); });
我们创建一个新的文件夹控件和一个ProductRating.js文件,它会控制我们的新控件。与我们的控制器和视图一样,自定义控件继承了SAPUI5基本对象的公共控件功能,对于控件来说,这是通过扩展基本类sap.ui.core.Control来完成的。
自定义控件是小的可重用组件,可以非常容易地在应用程序中创建。由于它们的性质,它们有时也被称为“记事本”或“动态”控件。自定义控件是一个JavaScript对象,它有两个特殊部分(元数据和呈现器)和许多实现控件功能的方法。
元数据部分定义数据结构,从而定义控件的API。通过这些关于控件SAPUI5的属性、事件和聚合的元信息,SAPUI5会自动创建setter和getter方法以及其他可以在应用程序中调用的方便函数。
渲染器定义HTML结构,当控件在视图中实例化时,该HTML结构将被添加到应用程序的DOM树中。它最初通常由SAPUI5的核心调用,并在控件的属性发生更改时调用。呈现函数的参数oRM是SAPUI5呈现管理器,可用于将字符串和控制属性写入HTML页面。
init方法是一个特殊的函数,每当控件实例化时,SAPUI5核心就会调用它。可以使用它来设置控件并准备要显示的内容。
请注意:控件总是扩展sap.ui.core。控制和呈现自己。您还可以扩展sa .ui.core。元素或sap.ui.base。如果希望重用SAPUI5的生命周期特性,包括未呈现的对象的数据绑定,可以直接使用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",newRatingIndicator({ value:this.getValue(), iconSize:"2rem", visualMode:"Half", liveChange:this._onRate.bind(this) })); this.setAggregation("_label",newLabel({ text:"{i18n>productRatingLabelInitial}" }).addStyleClass("sapUiSmallMargin")); this.setAggregation("_button",newButton({ 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.write("<div"); oRM.writeControlData(oControl); oRM.addClass("myAppDemoWTProductRating"); oRM.writeClasses(); oRM.write(">"); oRM.renderControl(oControl.getAggregation("_rating")); oRM.renderControl(oControl.getAggregation("_label")); oRM.renderControl(oControl.getAggregation("_button")); oRM.write("</div>"); } }); });
现在,我们使用所需的自定义功能增强了新的自定义控件。在我们的例子中,我们希望创建交互式产品评级,因此我们定义了一个值,并使用三个内部控件,这些控件由我们的控件自动更新显示。评级指示器控件用于收集用户对产品的输入,标签显示进一步的信息,按钮将评级提交给应用程序存储。
因此,在元数据部分,我们定义了几个在实现中使用的属性:
▪Properties
▪Value
我们定义一个控件属性值,该属性值将保存用户在评级中选择的值。该属性的Getter和setter函数将自动创建,如果愿意,还可以将其绑定到XML视图中的数据模型字段。
▪Aggregations
正如第一段所述,我们需要三个内部控制来实现我们的评级功能。因此,我们通过将可见性属性设置为hidden来创建三个“隐藏聚合”。这种方式, 我们可以使用视图上设置的模型,也可以在内部控件中使用,SAPUI5将负责生命周期管理,并在不再需要控件时销毁它们。聚合也可以用于保存控件数组,但是我们只想在每个聚合中有一个控件,因此需要通过将属性multiple设置为false来调整基数。
_rating: A sap.m.RatingIndicator control for user input
_label: A sap.m.Label to display additional information
_button: A sap.m.Button to submit the rating
请注意:您可以为控件定义聚合和关联。区别在于父控件与相关控件之间的关系:
▪aggregation:
聚合是一种强关系,它还管理相关控件的生命周期,例如,当父控件被销毁时,相关控件也会被销毁。而且,控件只能分配给一个聚合,如果它被分配给第二个聚合,它将自动从前一个聚合中删除。
▪association:
关联是一种弱关系,它不管理生命周期,并且可以定义多次。为了明确区别,关联只存储ID,而聚合存储对控件的直接引用。在本例中,我们不指定关联,因为我们希望内部控制由父控件管理。
▪Events
▪Change
我们指定一个更改事件,当提交评级时控件将触发该更改事件。它包含当前值作为事件参数。应用程序可以注册到这个事件并处理类似于“常规”SAPUI5控件的结果,这些控件实际上构建得类似于自定义控件。
在由SAPUI5在实例化控件的新实例时自动调用的init函数中,我们设置了内部控件。我们通过调用继承自sap.ui.core.Control的框架方法setAggregation来实例化这三个控件并将它们存储在内部聚合中。我们传递上面指定的内部聚合的名称和新的控件实例。我们指定了一些控件属性,以使自定义控件看起来更好,并将liveChange事件注册到评级,并将press事件注册到按钮。标签和按钮的初始文本引用自我们的i18n模型。
现在让我们忽略其他内部帮助函数和事件处理程序,定义我们的渲染器。在SAPUI5呈现管理器和作为引用传递的控件实例的帮助下,我们现在可以呈现控件的HTML结构。我们将外部<div>标记的开头呈现为<div,并调用助手方法writeControlData来呈现div标记内控件的ID和其他基本属性。接下来,我们添加一个自定义CSS类,以便稍后在CSS文件中定义自定义控件的样式规则。在视图中添加的这个CSS类和其他类,然后通过调用renderer实例上的writeClasses来呈现。然后关闭周围的div标记,并通过将内部聚合的内容传递给render managers renderControl函数呈现三个内部控件。这将调用控件的呈现程序并将它们的HTML添加到页面中。最后,关闭周围的<div>标记。
setValue是一个覆盖的setter。SAPUI5将生成一个setter,在控制器中调用或在XML视图中定义时更新属性值,但我们还需要更新隐藏聚合中的内部评级控制,以正确反映状态。此外,我们还可以通过调用setProperty方法来更新以true作为第三个参数的控件属性,从而跳过在控件上更改属性时通常触发的SAPUI5的重新发布。
现在我们为内部评级控制定义事件处理程序。每次用户更改评级时都会调用它。可以从sap.m.RatingIndicator 的事件参数值读取评级控制的当前值。通过调用覆盖setter来更新控件状态的值,然后更新评级旁边的标签,向用户显示当前选择的值,并显示最大值。带有占位符值的字符串是从自动分配给控件的i18n模型读取的。
接下来,我们拥有提交评级的评级按钮的press处理程序。我们假设对产品进行评级是一次性操作,首先禁用评级和按钮,这样用户就不允许提交其他评级。我们还更新标签以显示“感谢您的评级!”消息,然后触发控件的更改事件,并将当前值作为参数传入,以便侦听此事件的应用程序可以对评级交互作出反应。
我们定义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:ProductRatingid="rating"class="sapUiSmallMarginBeginEnd"change="onRatingChange"/> </Page> </mvc:View>
在detail视图上定义了一个新的名称空间wt,以便我们可以在视图中轻松地引用自定义控件。然后,我们将ProductRating控件的一个实例添加到详细信息页面,并为更改事件注册一个事件处理程序。为了获得合适的布局,我们还添加了一个margin样式类。
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: "/" + oEvent.getParameter("arguments").invoicePath, model: "invoice" }); }, onNavBack: function () { … }, onRatingChange :function(oEvent){ var fValue = oEvent.getParameter("value"); var oResourceBundle =this.getView().getModel("i18n").getResourceBundle(); MessageToast.show(oResourceBundle.getText("ratingConfirmation",[fValue])); } }); });
在onobjectmatchprivate方法中,我们调用reset方法,以便在显示不同项目的详细信息视图时提交另一个评级。在Detail控制器中,我们将依赖项加载到sap.m。MessageToast,因为我们将简单地显示一条消息,而不是将评级发送到后端,以保持示例的简单性。事件处理程序onRatingChange读取在提交评级时触发的自定义更改事件的值。然后,我们在MessageToast控件中显示带有值的确认消息。
webapp/css/style.css
.myAppDemoWTmyCustomButton.sapMBtn { margin-right: 0.125rem; } .myAppDemoWTmyCustomText { font-weight: bold; } /* ProductRating */ .myAppDemoWTProductRating { padding:0.75rem; } .myAppDemoWTProductRating .sapMRI { vertical-align: initial; }
我们也可以在渲染器中使用更多的HTML来实现这一点,但这是最简单的方法,它只应用于我们的自定义控件中。但是,请注意,自定义控件在您的应用程序中,可能需要在SAPUI5未来版本的内部控件更改时进行调整。要布局控件,我们向根类添加一些填充,以便在三个内部控件周围留出一些空间,并覆盖RatingIndicator控件的对齐,以便它与标签和按钮对齐在一行。
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
Conventions资源包由我们在自定义控件中引用的确认消息和字符串扩展。我们现在可以用全新的控件在详细页面上对产品进行评级。
将自定义控件放到应用程序的控件文件夹中。
Parent topic: Walkthrough
Previous: Step 33:
Routing Back and History
Next: Step 35: Responsiveness
Related Information
API Reference: sap.m.RatingIndicator
Samples: sap.m.RatingIndicator
API Reference: sap.ui.core.Control