You can view and download all files in the Samples in the Demo Kit at Routing and Navigation - Step 10 .
<mvc:View controllerName="sap.ui.demo.nav.controller.employee.Resume" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc"> <Page title="{i18n>ResumeOf} {FirstName} {LastName}" id="employeeResumePage" showNavButton="true" navButtonPress=".onNavBack"> <content> <IconTabBar id="iconTabBar" headerBackgroundDesign="Transparent" class="sapUiResponsiveContentPadding" binding="{Resume}" select=".onTabSelect" selectedKey="{view>/selectedTabKey}"> <items> <IconTabFilter id="infoTab" text="{i18n>tabInfo}" key="Info"> <Text text="{Information}"/> </IconTabFilter> <IconTabFilter id="projectsTab" text="{i18n>Projects}" key="Projects"> <mvc:XMLView viewName="sap.ui.demo.nav.view.employee.ResumeProjects"></mvc:XMLView> </IconTabFilter> <IconTabFilter id="hobbiesTab" text="{i18n>Hobbies}" key="Hobbies"> <!-- place content via lazy loading --> </IconTabFilter> <IconTabFilter id="notesTab" text="{i18n>Notes}" key="Notes"> <!-- place content via lazy loading --> </IconTabFilter> </items> </IconTabBar> </content> </Page> </mvc:View>
To illustrate lazy loading, we implement that the content is loaded only when the
user selects the corresponding tab for two of our tabs from the
IconTabBar
: Hobbies and
Notes. The IconTabFilter
controls each
have a hard-coded ID so that we can address them later in our routing configuration.
In real use cases, you would do this for tabs that contain a lot of content or
trigger expensive service calls to a back-end service.
In the resume
view we remove the content of the
Hobbies and Notes tabs as we will
now fill it dynamically with navigation features.
<mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Text text="{Hobbies}"/>
</mvc:View>
Create the file ResumeHobbies.view.xml
in the
webapp/view/employee
folder. Move the content for the tab that
was previously in the resume
view to that view. We don't need a
controller for this view as there is no additional logic involved. This view will be
lazy-loaded and placed into the content of the Hobbies tab
with navigation features.
<mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Text text="{Notes}"/>
</mvc:View>
Create the file ResumeNotes.view.xml
in the
webapp/view/employee
folder similar to the
Hobbies view to transform this tab to a separate view as
well.
sap.ui.define([
"sap/ui/demo/nav/controller/BaseController",
"sap/ui/model/json/JSONModel"
], function (BaseController, JSONModel) {
"use strict";
var _aValidTabKeys = ["Info", "Projects", "Hobbies", "Notes"];
return BaseController.extend("sap.ui.demo.nav.controller.employee.Resume", {
...
_onRouteMatched : function (oEvent) {
var oArgs, oView, oQuery;
oArgs = oEvent.getParameter("arguments");
oView = this.getView();
oView.bindElement({
...
});
oQuery = oArgs["?query"];
if (oQuery && _aValidTabKeys.indexOf(oQuery.tab) > -1){
oView.getModel("view").setProperty("/selectedTabKey", oQuery.tab);
// support lazy loading for the hobbies and notes tab
if (oQuery.tab === "Hobbies" || oQuery.tab === "Notes"){
// the target is either "resumeTabHobbies" or "resumeTabNotes"
this.getRouter().getTargets().display("resumeTab" + oQuery.tab);
}
} else {
// the default query param should be visible at all time
this.getRouter().navTo("employeeResume", {
employeeId : oArgs.employeeId,
"?query": {
tab : _aValidTabKeys[0]
}
}, true /*no history*/);
}
},
...
});
});
Now we extend the resume
controller a little and add additional
logic to the part of the _onRouteMatched
function where a new tab
has been selected and validated. In case the selectedKey
matches
Hobbies
or Notes
we call
this.getRouter().getTargets().display("resumeTab" + oQuery.tab)
to display the corresponding target manually. Here the valid targets are
resumeTabHobbies
and resumeTabNotes
as we have
changed the behavior for these two tabs by creating separate views.
These lines of code make sure that the targets are only loaded when they are needed ("lazy loading"). But the router does not know the new targets yet, so let's create them in our routing configuration.
{
"_version": "1.12.0",
"sap.app": {
...
},
"sap.ui": {
...
},
"sap.ui5": {
...
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"type": "View",
"viewType": "XML",
"path": "sap.ui.demo.nav.view",
"controlId": "app",
"controlAggregation": "pages",
"transition": "slide",
"bypassed": {
"target": "notFound"
}
},
"routes": [{
...
}, {
"pattern": "employees/{employeeId}/resume:?query:",
"name": "employeeResume",
"target": "employeeResume"
}],
"targets": {
...
"employeeResume": {
"id": "resume",
"name": "employee.Resume",
"level" : 4,
"transition": "flip"
},
"resumeTabHobbies": {
"id": "resumeHobbies",
"parent": "employeeResume",
"path": "sap.ui.demo.nav.view.employee",
"name": "ResumeHobbies",
"controlId": "hobbiesTab",
"controlAggregation": "content"
},
"resumeTabNotes": {
"id": "resumeNotes",
"parent": "employeeResume",
"path": "sap.ui.demo.nav.view.employee",
"name": "ResumeNotes",
"controlId": "notesTab",
"controlAggregation": "content"
}
}
}
}
}
We add the resumeTabHobbies
and resumeTabNotes
targets to the descriptor file with additional fields that override the default
configuration as we now want to display the targets locally inside the
IconTabBar
control and not as pages of the app.
The resumeTabHobbies
target sets the parent property to
employeeResume
. The parent property expects the name of another
target. In our case, this makes sure that the view from the parent target
employeeResume
is loaded before the target
resumeTabHobbies
is displayed. This can be considered as a
"view dependency". By setting the controlId
and
controlAggregation
properties the router places the view
ResumeHobbies
into the content
aggregation of
the IconTabFilter
control with ID hobbiesTab
. We
also set a parameter id
to a custom ID to illustrate how you could
overrule a hard-coded ID inside a view.
Each target can define only one parent with its parent property. This is
similar to the SAPUI5 control
tree where each control can have only one parent control (accessed with the
method getParent()
of
sap.ui.base.ManagedObject
). The
controlId
property always references a control inside
the parent view that is specified with the parent
target.
Now we add the resumeTabNotes
target similar to the
Hobbies
target. The resumeTabNotes
target
defines the parent target employeeResume
as well, because they
share the same parent view. We place the ResumeNotes
view into the
content
aggregation of the IconTabFilter
control with ID notesTab
.
We have now implemented lazy loading for the tabs Hobbies and Notes. These two tabs are now managed by the routing configuration and only loaded when we click on them the first time.
Try it out yourself: Open the Network tab of your browser's
developer tools and click on the tabs of your app. In the network traffic you will
see that ResumeHobbies.view.xml
file is only loaded when the
Hobbies tab is displayed the first time. The same applies
for the Notes tab. Mission accomplished!
Lazy-load content that is not initially displayed to the user