Get our custom app ready to use Fiori elements building blocks
To get our custom app ready to use Fiori elements building blocks we follow the steps described in the Flexible Programming model explorer
Modify the component
First we modify the component.js of our application: instead of extending “sap/ui/core/UIComponent” we modify it to extend “sap/fe/core/AppComponent”
Simplify the App structure
Most Custom Apps have an App view and an App Controller file but there is likely no app specific logic inside. Rather than implementing, your own root container for your app you could use the one provided under the hood by the “sap/fe/core/AppComponent”.
To do so you can get rid of the sap.ui5.rootView section of your manifest and of the sap.ui5.rooting.config section(except the viewPath you could either keep or put within each target definition).
Doing so you can get rid of the App.controller.js
and App.view.xml
files.
Modify the manifest to get our view ready for Fiori elements building block
Inside the manifest.json we modify the target definition for the page we want to adapt. In the routing.targets part of the manifest we had the following definition for our view:
"object": {
"viewName": "Object",
"viewId": "object",
"viewLevel": 2,
"title": "{i18n>objectViewTitle}"
},
We modify it like this:
"object": {
"type": "Component",
"id": "object",
"name": "sap.fe.core.fpm",
"options": {
"settings": {
"viewName": "leagues.view.Object",
"contextPath": "/Games"
}
},
"viewLevel": 2,
"title": "{i18n>objectViewTitle}"
},
We know use a sap.fe.core.fpm component that wraps our view that is passed in the settings. In the settings we also put in "contextPath": "/Games"
. We tell the component for our view that it should expect to have a context bound onto an element of the collection /Games
.
Modify the controller
Now we need to change our object page controller to inherit methods from sap.fe.core.PageController
. To do so we can modify the Base.controller: we replace sap.ui.define(["sap/ui/core/mvc/Controller",
by sap.ui.define(["sap/fe/core/PageController",
.
We can get rid of the setModel and getModel method that are already implemented by the PageController.
Inside each controller we make sure to call the onInit of the PageController:
onInit : function () {
PageController.prototype.onInit.apply(this);
...
We also need to modify the rooting code of our controllers. Via the Page controller our controllers have access to some routing methods. So we can modify the code like this in the Worklist controller.
// that.getRouter().navTo("object", {
that.routing.navigateToRoute("object", {
We also get rid of the getRouter method from the BaseController that relied on a sap.ui.core.UIComponent.
In the Object.controller the route matched code is now handled by our manifest definition with no additional code required.
Use a sap.fe.macros.Field
In the Object page view we would like to check with a simple building block that everything is correctly set up. To do so we add the building block namespace in the XML view: xmlns:macros="sap.fe.macros"
Then we replace a sap.M.Text by a Field Building Block:
<!--<Text text="{result}" />-->
<macros:Field metaPath="result" readOnly="true" id="resultField" />
Note that declaration is different: instead of giving a binding to the text property we give the path as a string and not as a binding to the metapath property.
Test it
Everything works and we do not see any difference.
If we look a bit closer however we might notice that the routing code of our controller is no longer used: this is because the manifest declaration of our route now takes care of the routing. We can now get rid off this part of our controller.
Leverage sap.fe.macros.Field to display inputs with Value help
Now we want to leverage features of the Field building block to have an Input with a Value Help. To do so, we first replace the input for the home team by a Field building block.
<!--<Input value="{team1}" />-->
<macros:Field metaPath="team1" readOnly="false" id="team1Input" />
Then we add some annotations to the team1 property. To do so we edit the file under app/webapp/localAnno.cds:
using FootballService from '../../srv/football-service';
annotate FootballService.Games with {
team1 @(
title: 'Home Team',
Common: {
Text: _team1.name, TextArrangement: #TextOnly,
ValueList : {
Label : 'Team Selection',
CollectionPath : 'Teams',
Parameters : [
{
$Type : 'Common.ValueListParameterInOut',
ValueListProperty : 'ID',
LocalDataProperty : team1
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'name'
}
]
}
}
);
}
With the Common.Text annotation the building block shows the team name while editing the foreign key on the Games entity. With the ValueList annotation it is able to show the ValueHelp knowing that the foreign key team1
need to get its value from one of the teams ID while showing the team name.
We can then do the same for the Away team input.
Additionally the building block Field provides standard Fiori features: in the Value Help the search is already there and operational without any code.
Leverage sap.fe.macros.Form to display the Form in both display and edit mode
Seeing that the Fiori elements building block Filed could handle both edit and display mode for my app I want to try to use the Form
building block to have all my field handled.
I add annotations to define a Form in my local annotation file:
annotate FootballService.Games with @UI:{
Facets:[
{
$Type : 'UI.ReferenceFacet',
ID : 'Game',
Label: 'Game',
Target: '@UI.FieldGroup#demo',
}
],
FieldGroup #demo:{
Label: 'Form Macro',
Data: [
{Label: 'Home team', Value:team1},
{Label: 'Away team', Value:team2},
{Label: 'Score', Value:result},
{Label: 'Date', Value:date}
]
}
};
I removed my Two forms:
<f:SimpleForm id="displayForm"
editable="false"
layout="ResponsiveGridLayout"
title="Game"
labelSpanXL="4"
labelSpanL="3"
labelSpanM="4"
labelSpanS="12"
adjustLabelSpan="false"
emptySpanXL="0"
emptySpanL="4"
emptySpanM="0"
emptySpanS="0"
columnsXL="2"
columnsL="1"
columnsM="1"
singleContainerFullSize="false"
>
<f:content>
<Label text="Home team" />
<Text id="nameText" text="{_team1/name}" />
<Label text="Away team" />
<Text text="{_team2/name}" />
<Label text="Score" />
<macros:Field metaPath="result" readOnly="true" id="resultField" />
<Label text="Date" />
<Text text="{date}" />
</f:content>
</f:SimpleForm>
<f:SimpleForm id="editForm"
editable="false"
layout="ResponsiveGridLayout"
title="Game"
labelSpanXL="4"
labelSpanL="3"
labelSpanM="4"
labelSpanS="12"
adjustLabelSpan="false"
emptySpanXL="0"
emptySpanL="4"
emptySpanM="0"
emptySpanS="0"
columnsXL="2"
columnsL="1"
columnsM="1"
singleContainerFullSize="false"
visible="false"
>
<f:content>
<Label text="Home team" />
<macros:Field metaPath="team1" readOnly="false" id="team1Input" />
<Label text="Away team" />
<macros:Field metaPath="team2" readOnly="false" id="team2Input" />
<Label text="Score" />
<Input value="{result}" />
<Label text="Date" />
<Text text="{date}" />
</f:content>
</f:SimpleForm>
and replace it by a macro Form:
<macros:Form metaPath="@com.sap.vocabularies.UI.v1.Facets/0" layoutMode="ResponsiveGridLayout" id="demoForm" />
In the controller code I modify what happens when someone click the edit button to use the model expected by the Fiori elements building block to store my edit state:
onEditPress: function () {
// var bEdit =oViewModel.getProperty("/edit");
// oViewModel.setProperty("/edit", !bEdit);
// this.getView().byId("displayForm").setVisible(bEdit);
// this.getView().byId("editForm").setVisible(!bEdit);
var oUIModel= this.getModel("ui");
var bEdit =oUIModel.getProperty("/isEditable");
oUIModel.setProperty('/isEditable', !bEdit);
},
Note that with this single building block we show the same content both in display and edit and still show the Value help we defined earlier.
The layout has changed but it now follows standard UX guidelines.
Add a sap.fe.macros.Table
If we wanted to add a Table for the game incidents we can try out the Table building block.
We add annotations for it in the local annotation file:
annotate FootballService.GameIncidents with @UI:{
LineItem:[
{Label: 'Type', Value:incidentType},
{Label: 'Time', Value:gameTime},
{Label: 'Description', Value:description},
{Label: 'Player', Value:player1.lastName},
{Label: 'Player', Value:player2.lastName}
]
};
And we add the building block inside our view:
<macros:Table metaPath="incidents/@com.sap.vocabularies.UI.v1.LineItem" readOnly="true" id="incidentsTable" />
Conclusion
We have seen how we can adapt a Custom App to use Fiori element building block. After configuration, this allows you to leverage standard Fiori design by just adding some annotations. In case you want to find out more, please have a look at our existing building blocks in the Flexible Programming Model Explorer and checkout Peter’s blog post on how these building blocks can also be used to extend Fiori Elements application.