实现和实例化 XML 片段
学习目标
处理定义为 XML 片段的对话框
可复用的 UI 部件
要在多个视图中使用的 UI 部件不容易定义。它们要么必须创建为新控件,要么必须创建为视图。将它们创建为新控件会带来开发开销,而将它们创建为单独的视图则会带来运行时开销。
为解决此问题,引入了片段(fragment)。它们支持复用和视图模块化,且不会增加开销。
观看此视频以了解更多关于片段主要特征的内容。
视频:本节含 SAP 官方视频(视频 ID
1_eiifj1my),需在 learning.sap.com 在线观看。
注意
除了在单独的文件中定义片段外,也可以内联定义片段并立即实例化。不过一般而言,片段的内联定义只起次要作用。
XML 片段
本课程仅涉及 XML 片段,因为它们最为常见。有关 JavaScript 片段和 HTML 片段,请参阅文档。
XML 片段与 XML 视图类似,但没有作为根元素的 <View> 标签。
Simple XML Fragment 图展示了 XML 片段定义的一个示例。

包含 XML 片段定义的文件扩展名为 **.fragment.xml*。它由 SAPUI5 运行时通过其 SAPUI5 模块名加载(见下文)。
在示例中,<core:FragmentDefinition> 标签用作片段的根元素。如果没有这个 <core:FragmentDefinition> 标签,该片段将把 Text 控件和 Button 控件作为根控件。但这是不可能的,因为 XML 文档必须始终恰好有一个根节点。
<core:FragmentDefinition> 标签在运行时在 HTML 中没有对应的表示;它的子元素会直接添加到放置片段的位置。
如果片段只包含一个根控件,则 <core:FragmentDefinition> 标签的使用是可选的。
示例中按钮的 press 事件绑定到控制器的 onDoSomething 事件处理程序。这意味着该片段必须使用具有此方法的控制器来实例化。
片段实例化
在 XML 视图中实例化片段
Declarative Use of Fragments 图中的示例展示了一个 XML 视图,它通过 <core:Fragment> 标签包含了上面所示的 XML 片段。

该标签的 fragmentName 属性包含所用片段的 SAPUI5 模块名。
type 属性用于传递片段的类型。这意味着任何类型的片段都可以嵌入 XML 视图,不仅限于 XML 片段,还包括 JavaScript 和 HTML 片段。
借助可选的 id 属性,可以为片段实例传递一个 Id。此 Id 用作片段实例中所有控件 Id 的前缀。这样,例如当同一个片段被两次嵌入同一个视图时,就可以避免 Id 重复错误。
注意
有关唯一 Id 的详细信息,请参阅文档。
所实现的片段引用最终就像一条导入语句,把片段的内容控件包含到视图中。
当片段嵌入 XML 视图时,该视图的控制器也会自动设置为片段的控制器。由于在此处讨论的示例中,片段包含一个按钮,其 press 事件绑定到事件处理程序 onDoSomething,因此该方法必须存在于嵌入视图的控制器中。
通过 API 实例化片段
SAPUI5 提供两种通过 API 实例化片段的选项。

如果要在视图控制器中(更确切地说,在扩展了 sap.ui.core.mvc.Controller 的控制器中)实例化片段,可以使用 loadFragment 方法。它在任何视图控制器中都可用。该方法会将视图控制器作为片段控制器传递给所创建的片段。也就是说,片段中引用的事件处理程序方法会在视图控制器上调用。
loadFragment 的使用示例将在后面讨论。
在控制器之外,可以使用通用方法 sap.ui.core.Fragment.load 进行实例化。除其他用途外,该方法可以接收一个 JavaScript 对象作为片段控制器。这里任何对象都可以充当片段控制器,前提是它具有片段所需的方法。
有关上述两个方法的更多信息,请参阅 Demo Kit 中的 API Reference。
作为片段的对话框
对话框比较特殊,因为它们打开在常规应用程序内容之上,因此不属于某个特定视图。此外,对话框可以在应用程序的多个视图中使用。
视图不支持此类对话框的实现,但片段非常适合用于定义对话框以及不属于常规页面 UI 结构的其他弹出控件。
要使用片段定义弹出窗口,只需让片段的根控件是对话框或类似控件即可。

Dialog as XML Fragment 图展示了一个使用 XML 片段定义的简单对话框。
该片段的根控件是 sap.m.Dialog UI 元素。此控件用于提示用户执行操作或进行确认。当对话框在应用程序中打开时,它会中断当前的应用程序处理,因为它是唯一获得焦点的 UI 元素,并且主屏幕被阻止。
对话框的内容完全可自定义,通过 content 聚合定义。在示例中,为简单起见,对话框仅包含一个文本为 Hello World 的 Text UI 元素。
beginButton 聚合可用于定义将显示在对话框页脚区域的操作按钮。在示例中,它是一个文本为 Ok 的按钮,其 press 事件绑定到事件处理程序 onCloseDialog。
该对话框稍后将通过视图控制器实例化,其中视图控制器将被设置为片段控制器。这意味着视图控制器必须具有方法 onCloseDialog,用于关闭已打开的对话框。
在示例中,为 Dialog UI 元素的 id 属性指定了值 dialog。此 Id 稍后将在视图控制器中用于访问所创建的对话框实例。
使用定义为片段的对话框
为了在视图控制器中打开上面所示的对话框,必须首先加载相应的片段。
这可以使用 loadFragment 方法完成,它在任何视图控制器中都可用(见上文)。Opening and Closing a Dialog 图中的 anyControllerMethod 展示了如何使用 loadFragment 方法。

loadFragment 方法接收要加载的片段的模块名(在示例中为 sap.training.exc.view.Dialog)。默认情况下,loadFragment 假定该片段是 XML 片段。其他片段类型可以通过 type 属性传递给该方法。
loadFragment 方法返回一个 Promise,它解析为片段内容,即 Dialog 实例。在示例中,由 loadFragment 返回的 Promise pDialog 被存储为控制器属性(this.pDialog)。借助此控制器属性,if 语句确保 loadFragment 方法只在首次处理 anyControllerMethod 时被调用一次。这是因为在首次处理 anyControllerMethod 之后,this.pDialog 不再是初始值,因此 if 块不再被执行。
如前所述,该 Promise 解析为片段内容,即 Dialog 实例。在此 Dialog 实例上调用 open 方法以打开对话框。
loadFragment 方法将视图控制器作为片段控制器传递给所加载的片段。因此,示例中 XML 片段内的按钮所引用的事件处理程序 onCloseDialog 必须在视图控制器上实现(参见 Opening and Closing a Dialog 图)。
loadFragment 方法确保片段中使用的 Id 以视图实例的 Id 为前缀,以避免 Id 重复问题。这使得片段中使用的控件可以通过控制器的 byId 方法访问。为此,将片段中分配的控件 Id 传递给 byId 方法。在示例中,通过这种方式访问所创建的 Dialog 实例,并在其上调用 close 方法以再次关闭已打开的对话框。
同步内容密度
对话框打开在 DOM 的一个特殊部分,称为静态区域。因此,它不属于打开它的那个视图。结果是,为视图设置的内容密度 CSS 类不会自动转发到对话框。因此,必须手动将视图的相应样式类传递给对话框。
可以使用 sap/ui/core/syncStyleClass 函数来完成此操作,如 Forwarding the Content Density to a Dialog 图所示。

在图中,sap/ui/core/syncStyleClass 函数已添加到相应视图控制器的依赖项数组中,并且工厂函数已扩展了参数 syncStyleClass。
样式类的同步在方法 anyControllerMethod 中完成,该方法用于加载 XML 片段并打开对话框,如上所述。为了将内容密度传递给对话框,已向此方法添加了高亮显示的代码。
添加的代码注册了一个回调函数,当片段加载成功完成时执行该回调函数。对话框实例被传递给此回调函数。
通过在添加的回调函数上调用 bind 方法,可以使回调函数实现中的 this 关键字具有传递给 bind 方法的值。也就是说,在回调函数内部 this 引用视图控制器。
在回调函数中,使用 sap/ui/core/syncStyleClass 函数将为视图设置的内容密度应用到对话框实例。为此,通过组件控制器的 getContentDensityClass 方法查询应用程序当前使用的内容密度。
注意
getContentDensity 方法必须由开发人员事先创建并相应实现(见上文)。
有关如何使用 sap/ui/core/syncStyleClass 函数的详细信息,请参阅 Demo Kit 中的 API Reference。
为了链式调用 then 方法,回调函数返回对话框实例 oDialog。这确保了该实例被传递给下一个注册的完成回调。
使用片段实现弹出窗口
业务场景
在本练习中,你将使用 XML 片段实现一个带有信息文本和用于关闭它的 Ok 按钮的弹出窗口。当用户在 Overview 视图上按下 Create Customer 按钮时,应显示该对话框。你还会将上一练习中设置的内容密度 CSS 类也应用到该对话框。*Template:*Git Repository: https://github.com/SAP-samples/sapui5-development-learning-journey.git, Branch: **sol/9_content_density***Model solution:*Git Repository: https://github.com/SAP-samples/sapui5-development-learning-journey.git, Branch: sol/10_fragments
任务 1:创建包含对话框定义的 XML 片段
步骤
在
*webapp*文件夹的子文件夹*view*中创建一个名为*Dialog.fragment.xml*的新文件。在项目结构中打开
*webapp/view*文件夹的上下文菜单。选择 New File。
在出现的字段中,输入
Dialog.fragment.xml并按 Enter。
结果
Dialog.fragment.xml
文件已创建并显示在编辑器中。
2. 将以下代码添加到 *Dialog.fragment.xml* 文件中,以使用 XML 片段定义对话框:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<Dialog
id="dialog"
title="Info" type="Message">
<content>
<Text text="Customer data is later saved via an OData service."/>
</content>
<beginButton>
<Button
text="Ok"
press=".onCloseDialog"/>
</beginButton>
</Dialog>
</core:FragmentDefinition>
注意
对话框会向用户显示文本"Customer data is later saved via an OData service"。此外,它有一个 Ok 按钮,用户可以用它再次关闭弹出窗口。为此,将为 Ok 按钮的 press 事件注册的事件处理程序方法 onCloseDialog 将在稍后实现。另请注意 sap.m.Dialog UI 元素的 Id dialog。此 Id 稍后将在 onCloseDialog 事件处理程序方法中用于访问该弹出窗口。
结果
XML 片段应按如下方式实现:

任务 2:为 Overview 视图上按钮的 press 事件注册事件处理程序
步骤
在编辑器中打开
*webapp/view*文件夹中的*Overview.view.xml*文件。在 Create Customer 按钮的
<Button>标签中添加属性press=".onSave",以在此按钮的press事件上注册名为onSave的事件处理程序。
注意
在下一步中,你将在视图控制器上实现此事件处理程序,以打开上面创建的弹出窗口。
结果
Create Customer 按钮现在应按如下方式实现:

任务 3:在视图控制器上实现所注册的事件处理程序以打开弹出窗口
步骤
在编辑器中打开
*webapp/controller*文件夹中的*Overview.controller.js*文件。将上一步中为 Create Customer 按钮注册的
onSave事件处理程序方法添加到视图控制器中。按如下方式实现此方法,以便在其中打开上面定义的弹出窗口:
onSave: function () {
if (!this.pDialog) {
this.pDialog = this.loadFragment({
name: "sap.training.exc.view.Dialog"
});
}
this.pDialog.then(function (oDialog) {
oDialog.open();
});
}
注意
如果片段中的对话框尚不存在,则通过调用
loadFragment()方法实例化该片段。对话框片段的加载
Promise存储在视图控制器实例上。这样就可以在每次点击 Create Customer 按钮时以异步方式处理对话框的打开。
结果
视图控制器现在应如下所示:

任务 4:在视图控制器上实现关闭对话框的方法
步骤
确保
*Overview.controller.js*视图控制器已在编辑器中打开。将上面为弹出窗口上的 Ok 按钮注册的
onCloseDialog事件处理程序方法添加到视图控制器中。按如下方式实现此方法,以便通过它再次关闭弹出窗口:
onCloseDialog: function () {
this.byId("dialog").close();
}
注意
该事件处理程序方法通过其 Id 访问对话框来关闭弹出窗口。无需链式调用 pDialog Promise,因为该事件处理程序只会从已加载的对话框内部调用。
结果
视图控制器现在应如下所示:

任务 5:为对话框应用内容密度
步骤
确保
*Overview.controller.js*视图控制器已在编辑器中打开。将
sap/ui/core/syncStyleClass模块添加到视图控制器的依赖项数组中,并向视图控制器的工厂函数添加一个名为syncStyleClass的相应参数。
结果
视图控制器现在应如下所示:
3. 在 onSave 事件处理程序方法中使用 sap/ui/core/syncStyleClass 模块,将内容密度转发到弹出窗口。为此,请按如下方式调整事件处理程序方法的实现:
onSave: function () {
if (!this.pDialog) {
this.pDialog = this.loadFragment({
name: "sap.training.exc.view.Dialog"
}).then(function (oDialog) {
syncStyleClass(this.getOwnerComponent().getContentDensityClass(), this.getView(), oDialog);
return oDialog;
}.bind(this));
}
this.pDialog.then(function (oDialog) {
oDialog.open();
});
}
注意
该弹出窗口不属于 Overview 视图,而是在 DOM 的一个特殊部分(静态区域)中打开。因此,在 App 视图中定义的内容密度类对弹出窗口不可见,所以需要手动将应用的样式类与弹出窗口同步。
结果
onSave
事件处理程序方法现在应如下所示:
4. 通过从 SAP Business Application Studio 启动应用程序来进行测试运行。
检查以下几点:
确保在按下 Create Customer 按钮时弹出窗口会打开。
确保在按下对话框上的 Ok 按钮时弹出窗口会关闭。
确保内容密度 compact 和 cozy 已传递到对话框。对于 cozy ,对话框上的 Ok 按钮应显示得比 compact 更大。
注意
要测试不同的内容密度,你可以使用 Google Chrome、Firefox 或 Microsoft Edge 浏览器开发者工具中的设备工具栏:启动应用程序,然后在开发者工具(F12)中通过组合键 Ctrl + Shift + M 调出设备工具栏。你可以使用设备工具栏选择要模拟的设备。选择设备后,必须刷新浏览器(F5),以确保调用 App 视图控制器的
onInit()方法,该方法会针对设备类型专门设置内容密度。注意 请不要在意开发者工具控制台中显示的错误。这是由于一些为后续练习准备的代码当时尚未完成。
右键单击 sapui5-development-learning-journey 项目中的任意子文件夹,然后从出现的上下文菜单中选择 Preview Application。
在出现的对话框中选择名为 start-noflp 的 npm 脚本。
在打开的应用程序中,检查组件是否按预期工作。