Render Cards dynamically via XML view - sapui5

Is there a way to render the sap.f.Card control dynamically with an XML view? With aggregation binding maybe? I only used aggregation binding with lists and tables so far...
I need to render some cards dynamically on a main view but I want to stay with the MVC concept. That's why I don't prefer to render the cards in the controller. My Card is also very complex and has multiple controls in it. (Text, Status Indicator, Progress Indicator, etc.)
Is there a way of implementing that?

In Fiori, Cards are usually rendered within sap.f.GridContainer (In fact, it was initially called sap.f.CardContainer).
Just like with Tables and Lists, you can bind <items> to the GridContainer too with the Card as a template control:
<f:GridContainer xmlns:f="sap.f"
width="100%"
snapToRow="true"
items="{
path: '/myCardItems',
templateShareable: false
}">
<f:layout>
<f:GridContainerSettings rowSize="84px" columnSize="84px" gap="8px" />
</f:layout>
<f:layoutXS>
<f:GridContainerSettings rowSize="70px" columnSize="70px" gap="8px" />
</f:layoutXS>
<f:items>
<f:Card> <!-- template -->
<f:header>
<card:Header xmlns:card="sap.f.cards" title="{title}" />
</f:header>
<f:layoutData>
<f:GridContainerItemLayoutData minRows="..." columns="..." />
</f:layoutData>
</f:Card>
</f:items>
</f:GridContainer>
The above snippet is from the Demo Kit sample that has an aggregation binding.

You can achieve it using the Grid layout withcontent aggregation
view.xml
<core:View
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:l="sap.ui.layout"
xmlns:fc="sap.f"
xmlns:card="sap.f.cards"
controllerName="path.Main"
xmlns:html="http://www.w3.org/1999/xhtml">
<Page title="Main" class="sapUiContentPadding">
<content>
<!-- Cards data binding -->
<l:Grid containerQuery="true" id="cardsGrid" defaultSpan="XL2 L4" class="gridProgressIndicator" hSpacing="0" content="{/cards}">
<fc:Card class="sapUiMediumMargin" width="300px">
<fc:header>
<card:Header
title="{title}"
subtitle="{subTitle}"
iconSrc="{icon}"/>
</fc:header>
<fc:content>
<VBox
height="115px"
class="sapUiSmallMargin"
justifyContent="SpaceBetween">
<HBox justifyContent="SpaceBetween">
<ComboBox
width="120px"
placeholder="To City"
items="{items}">
<core:Item key="{key}" text="{text}" />
</ComboBox>
<ComboBox
width="120px"
placeholder="To City"
items="{items}">
<core:Item key="{key}" text="{text}" />
</ComboBox>
</HBox>
<HBox justifyContent="SpaceBetween">
<DatePicker width="186px" placeholder="Choose Date ..."/>
<Button text="Book" press="onBookPress" type="Emphasized" />
</HBox>
</VBox>
</fc:content>
</fc:Card>
</l:Grid>
</content>
</Page>
</core:View>
controller.js
bindCardsGrid: function() {
var rowData = [
{ "title": "Title 1", "subTitle": "SubTitle 1", "icon": "sap-icon://menu2", "items": [{ "key": "CV1", "text": "CV1"}, { "key": "CV2", "text": "CV2"}, { "key": "CV3", "text": "CV3"},{ "key": "CV4", "text": "CV4"}]},
{ "title": "Title 2", "subTitle": "SubTitle 2", "icon": "sap-icon://add-contact", "items": [{ "key": "CV1", "text": "CV1"}, { "key": "CV2", "text": "CV2"}, { "key": "CV3", "text": "CV3"},{ "key": "CV4", "text": "CV4"}]},
{ "title": "Title 3", "subTitle": "SubTitle 3", "icon": "sap-icon://business-objects-experience", "items": [{ "key": "CV1", "text": "CV1"}, { "key": "CV2", "text": "CV2"}, { "key": "CV3", "text": "CV3"},{ "key": "CV4", "text": "CV4"}]},
{ "title": "Title 4", "subTitle": "SubTitle 4", "icon": "sap-icon://process", "items": [{ "key": "CV1", "text": "CV1"}, { "key": "CV2", "text": "CV2"}, { "key": "CV3", "text": "CV3"},{ "key": "CV4", "text": "CV4"}]}
];
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({ "cards": rowData });
this.getView().setModel(oModel);
},
output

Related

How to change aggregation binding path dynamically when pressing a button

<mvc:View xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
controllerName="z.controller.Main">
<Page id="page" title="ComboBox" >
<content>
<ComboBox name="Drop-down List" id="box0" items="{/country}">
<items >
<core:Item key="{Key}" text="{Name}" id="item0"/>
</items>
</ComboBox>
</content>
<Button text="Change" press="onChange"/>
</Page>
</mvc:View>
model1.json
{
"country": [{
"Key": "IN",
"Name": "India"
},
{
"Key": "US",
"Name": "USA"
},{
"Key": "UK",
"Name": "United Kingdom"
}]
}
model2.json
{
"country2": [{
"Key": "BD",
"Name": "Bangladesh"
},
{
"Key": "CA",
"Name": "Canada"
},{
"Key": "FR",
"Name": "France"
}]
}
Main.controller.js
sap.ui.define([
'sap/ui/core/mvc/Controller',
"sap/ui/model/json/JSONModel",
"z/models/model"
], function(Controller,JSONModel, localModel) {
'use strict';
return Controller.extend("z.controller.Main",{
onInit: function(){
var oModel = localModel.createJSONModel("models/data/model1.json");
sap.ui.getCore().setModel(oModel);
var oModel2 = localModel.createJSONModel("models/data/model2.json");
sap.ui.getCore().setModel(oModel2, "model2");
},
onChange: function(){
}
})
});
controller.js
sap.ui.define([
'sap/ui/core/mvc/Controller',
"sap/ui/model/json/JSONModel"
], function(Controller,JSONModel) {
'use strict';
return {
createJSONModel: function(filePath){
var oModel = new JSONModel();
oModel.loadData(filePath);
return oModel;
}
}
});
I have two JSON models and building a combo list using aggregation binding(items="{/country}") by reading data from model1. I am trying to fill the same Combolist by reading the data from the model2 on a button press event.
You can switch the model by using it as a prefix model2> (as you have defined in the Main.controller.js in the onInit method) in the binding path:
onChange: function() {
var oComboBox = this.getView().byId("box0");
oComboBox.bindItems("model2>/country2", {
template: this.getView().byId("item0")
});
}
And change the aggregation from items to dependents.
<ComboBox name="Drop-down List" id="box0" items="{/country}">
<dependents>
<core:Item key="{Key}" text="{Name}" id="item0"/>
</dependents>
</ComboBox>

SAPUI5 text is not displayed in Selector

I read the documentation https://sapui5.hana.ondemand.com/#/entity/sap.m.Select/sample/sap.m.sample.Select/code and I wanted to created a selector in my code.
I carefully follow the documentation but the text isn't display.
In my XML file:
<Select
selectedKey="{test>/SelectedProduct}"
items="{
path: 'test>/ProductCollection',
sorter: { path: 'Name' }
}">
<core:Item key="{test>/SelectedProduct/ProductId}" text="{test>/SelectedProduct/Name}" />
</Select>
in my controller file:
var oData = {
"SelectedProduct": "HT-1001",
"SelectedProduct2": "HT-1001",
"SelectedProduct3": "HT-1001",
"ProductCollection": [
{
"ProductId": "HT-1000",
"Name": "Notebook Basic 15"
},
{
"ProductId": "HT-1001",
"Name": "Notebook Basic 17"
},
{
"ProductId": "HT-1002",
"Name": "Notebook Basic 18"
},
{
"ProductId": "HT-1002",
"Name": "Notebook Basic 18"
},
{
"ProductId": "HT-1002",
"Name": "Notebook Basic 18"
}
],
"Editable": true,
"Enabled": true
};
var oModel = new JSONModel(oData);
this.getView().setModel(oModel, "test");
It doesn't display me the text but it display me the number of elements of my array.
I also tried:
<Select
selectedKey="{test>/SelectedProduct}"
items="{
path: 'test>/ProductCollection',
sorter: { path: 'Name' }
}">
<core:Item key="{test>/ProductCollection/ProductId}" text="{test>/ProductCollection/Name}" />
</Select>
And:
<Select
selectedKey="{test>/SelectedProduct}"
items="{
path: 'test>/ProductCollection',
sorter: { path: 'Name' }
}">
<core:Item key="{test>/ProductCollection.ProductId}" text="{test>/ProductCollection.Name}" />
</Select>
Why this solution doesn't work as expected ?
No need for ProductCollection in Item binding path. The item path is already set to ProductCollection in Select.
<core:Item key="{test>ProductId}" text="{test>Name}" />

Cant load the image src link from the JSON file in the view of the project

I am able to bind the CourseName and Text to the view but is not able to bind the Image link to the view (in the Image src)
code for view is as follows :-
<mvc:View controllerName="Workspace.controller.MasterData" xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc"
xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form">
<Shell>
<App class="sapUiResponsiveMargin" width="auto">
<pages>
<Page title="{i18n>MasterDataTitle}" backgroundDesign="Transparent" showNavButton="true" navButtonPress="onNavBack">
<l:Grid class="sapUiSmallMarginTop" hSpacing="2" defaultSpan="L6 M6 S10" id="grid1">
<l:content>
<VBox items="{course>/Courses}">
<items>
<Image src="{course>Image}" width="100%" press="CreateDataButton">
<layoutData>
<l:GridData span="L3 M3 S8" linebreakL="true" linebreakM="true" linebreakS="true"/>
</layoutData>
</Image>
<VBox >
<Text text="{course>CourseName}"/>
<Text text="{course>Text}"/>
</VBox>
</items>
</VBox>
</l:content>
</l:Grid>
</Page>
</pages>
</App>
</Shell>
The JSON file is as follows :- (course.json)
{
"Courses": [{
"CourseName": "JAVA",
"Text": "Java is a general-purpose computer-programming language ",
"Image" :"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI4AAACgCAMAAADthaR8AAAAwFBMVEX"
}, {
"CourseName": "C++",
"Text": "Hypertext Preprocessor is a server-side scripting language designed for web development. ",
"Image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI4AAACgCAMAAADthaR8AAAAwFBMVEX"
}, {
"CourseName": "PHP",
"Text": "C++ is a general-purpose programming language. ",
"Image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJoAAABNCAMAAABpGo2yAAAAY1BMVEX///8AAAA/Pz/r6+uFhYX39"
}, {
"CourseName": "JAVASCRIPT",
"Text": "SQL is a domain-specific language used in programming and designed .",
"Image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAMAAAC8EZcfAAAAeFBMVEX33x4AAAD74x9JQgnUwBrx2h0sKAX/6B/+5R+WiBL/6h"
}, {
"CourseName": "SQL",
"Text": "SQL is a domain-specific language used in programming and designed ",
"Image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAO0AAADVCAMAAACMuod9AAAA+VBMVEX///8mh84Acsa6ury41DJ/ugAAacO80usUeskkhs4kitC2trgAf8vv7+"
}]
}

Having trouble in routing with parameters in SAPUI5

I am trying to show the data of a particular item in a list when ,clicked upon , on the next page (view)
Here Overview.view.xml is the default view with a clickable list connected to an oData model.
Next is the controller for Overview navigating to the next page i.e. Overview.controller.js.
The next page is Detail.view.xml
I want to show details of clicked item in this page.
#Overview.view.xml
<mvc:View controllerName="root.demo.controller.Overview" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
displayBlock="true" xmlns="sap.m">
<App >
<pages>
<Page title="{i18n>title}">
<content>
<List headerText="list" class="sapUiResponsiveMargin" width="auto" items="{HANA>/person}">
<items>
<ObjectListItem title="{HANA>ID} x {HANA>FIRSTNAME}" type="Navigation" press="onPress"/>
</items>
</List>
</content>
</Page>
</pages>
</App>
</mvc:View>
#Overview.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function (Controller) {
"use strict";
return Controller.extend("root.demo.controller.App", {
onPress: function (oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("detail");
}
});
});
#Detail.view.xml
<mvc:View xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Page title="{i18n>detailPageTitle}">
<ObjectStatus title="PAGE 1"
/>
</Page>
</mvc:View>
If you have different Entity Sets with association you need to send the key of the association to the second view and there create the binding towards the assocaited entity set
If not you need to send the object to the second view
Either way you need to navigate with parameters
In the manifest file you need to specify the routing
"routes": [
{
"name": "TargetMaster",
"pattern": "RouteMaster",
"target": [
"TargetMaster"
]
},
{
"pattern": "Detail/{storePath}",
"name": "Detail",
"target": "Detail"
}
],
"targets": {
"TargetMaster": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewName": "Master"
},
"Detail": {
"viewType": "XML",
"viewName": "Detail"
}
}
In the Overview Controller you need to create the navigation similarly to
onListItemPress: function(oEvent){
var oItem = oEvent.getSource();
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("Detail",{
storePath: oItem.getBindingContext().getProperty("Store")
});
}
you can find more information on the official documentation here

Filtering on Nested Aggregation Binding

I want to be able to apply a filter to a nested aggregation binding, but it doesn't seem to work. Here is the XML:
<l:Grid id="test" defaultSpan="L6 M6 S6" content="{path : 'test>/', templateShareable:false}">
<l:content>
<VBox width="100%">
<HBox height="100px" alignItems="Center" justifyContent="Start">
<VBox alignItems="Center" width="25%">
<core:Icon src="{test>icon}" width="100%" />
<Text text="{test>text}" width="100%"/>
</VBox>
<VBox id="test" height="80px" items="{path: 'test>data/', templateShareable:false}">
<Link text="{parts: [{ path: 'test>key'},
{ path: 'test>value' }],
formatter : 'dostuff'}"/>
</VBox>
</HBox>
</VBox>
</l:content>
</l:Grid>
And my JSON data is as follows:
{
"results": [{
"text": "object1",
"icon": "icon1",
"data": [{
"value1": "foo",
"value2": "bar"
}, {
"value1": "john",
"value2": "smith"
}]
},
{
"text": "object2",
"icon": "icon2",
"data": [{
"value1": "adam",
"value2": "bobson"
}, {
"value1": "john",
"value2": "smith"
}, {
"value1": "whatever",
"value2": "work please"
}]
}
]
}
I want to be able to filter on test>/results/[n]/data/[n]/[value1 + value2] and have the gird filtered at that level. Whatever I try, it only filters on the Grid content because I can't seem to get the binding for the in VBox "test".
Cheers,
James
Are you trying to filter the content dynamically from JS? Or statically from the XML directly? I assume that you want to do it from JS.
Your VBox with id test is inside a Grid whose content aggregation you have binded. This means that the whole VBox inside the grid (including the test VBox) is treated as a template for the content aggregation of the grid (i.e. with our JSON, you will end up having two copies of the VBox).
Using this.byId("test") will clearly not work, as it will return a control which is part of the Grid's content aggregation template. So be able to filter the inner VBoxes (because there will be several of them depending on the contents of the model), you have to iterate through the contents of the grid and apply the filtering to each of the target VBoxes. Something along the lines of:
this.byId("grid").getContent().forEach(function(oVbox) {
// need to get the HBox and the second VBox from it.
oVbox.getItems()[0].getItems()[1].getBinding("items").filter(...);
});
Also, both the Grid and the VBox have the same IDs, so your code will fail because of that as well. Generally, you don't need to define an ID for an aggregation template or a child.