I have a problem, that sometimes the controller does not get loaded:
and you can see the error message. It should load the following controller(marked with red border):
Why it is not getting loaded sometimes?
When the controller gets loaded, it looks like:
UPDATE:
I am using flexible column layout.
The App.view.xml looks like:
<mvc:View controllerName="ch.mindustrie.ZMM_CLASSIFICATION.controller.App" xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m" xmlns:f="sap.f">
<App id="root">
<f:FlexibleColumnLayout id="idClassLayout"/>
</App>
</mvc:View>
the manifest.json file:
{
"_version": "1.9.0",
"sap.app": {
"id": "ch.mindustrie.ZMM_CLASSIFICATION",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "0.1.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"sourceTemplate": {
"id": "servicecatalog.connectivityComponentForManifest",
"version": "0.0.0"
},
"dataSources": {
"ZMM_CLASSIFICATION_SRV": {
"uri": "/sap/opu/odata/sap/ZMM_CLASSIFICATION_SRV/",
"type": "OData",
"settings": {
"odataVersion": "2.0",
"localUri": "localService/ZMM_CLASSIFICATION_SRV/metadata.xml"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes": [
"sap_hcb",
"sap_belize"
]
},
"sap.ui5": {
"handleValidation": true,
"rootView": {
"viewName": "ch.mindustrie.ZMM_CLASSIFICATION.view.App",
"type": "XML",
"async": true,
"id": "app"
},
"dependencies": {
"minUI5Version": "1.50.0",
"libs": {
"sap.ui.layout": {},
"sap.ui.core": {},
"sap.m": {},
"sap.f": {},
"sap.collaboration": {
"lazy": true
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "ch.mindustrie.ZMM_CLASSIFICATION.i18n.i18n"
}
},
"Classification": {
"uri": "/sap/opu/odata/sap/ZMM_CLASSIFICATION_SRV/",
"type": "sap.ui.model.odata.v2.ODataModel",
"settings": {
"defaultOperationMode": "Server",
"defaultBindingMode": "OneWay",
"defaultCountMode": "Request"
},
"dataSource": "ZMM_CLASSIFICATION_SRV",
"preload": true
}
},
"resources": {
"css": [
{
"uri": "css/style.css"
}
]
},
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"viewType": "XML",
"viewPath": "ch.mindustrie.ZMM_CLASSIFICATION.view",
"controlId": "idClassLayout",
"bypassed": {
"target": [
"search",
"characteristic"
]
},
"async": true
},
"routes": [
{
"pattern": "",
"name": "search",
"target": [
"characteristic",
"search"
],
"layout": "TwoColumnsBeginExpanded"
},
{
"pattern": "search/{internalclassnum}",
"name": "characteristic",
"target": [
"search",
"characteristic"
],
"layout": "TwoColumnsMidExpanded"
}
],
"targets": {
"search": {
"viewName": "Search",
"viewLevel": 1,
"viewId": "search",
"controlAggregation": "beginColumnPages"
},
"characteristic": {
"viewName": "Characteristic",
"viewLevel": 2,
"viewId": "characteristic",
"controlAggregation": "endColumnPages"
}
}
}
},
"sap.platform.abap": {
"uri": "/sap/bc/ui5_ui5/sap/zmm_classifi/webapp",
"_version": "1.1.0"
}
}
the Search.view.xml:
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:uxap="sap.uxap"
xmlns:f="sap.ui.layout.form" xmlns:smartFilterBar="sap.ui.comp.smartfilterbar"
controllerName="ch.mindustrie.ZMM_CLASSIFICATION.controller.Search" xmlns:html="http://www.w3.org/1999/xhtml">
<smartFilterBar:SmartFilterBar id="SelectionFilterBar" entitySet="ZMM_C_CLASSIFICATION" search="onSearchClass">
<smartFilterBar:controlConfiguration>
<smartFilterBar:ControlConfiguration key="ClassType" visibleInAdvancedArea="true" preventInitialDataFetchInValueHelpDialog="false"></smartFilterBar:ControlConfiguration>
<smartFilterBar:ControlConfiguration key="ClassNum" visibleInAdvancedArea="true" preventInitialDataFetchInValueHelpDialog="false"></smartFilterBar:ControlConfiguration>
</smartFilterBar:controlConfiguration>
</smartFilterBar:SmartFilterBar>
<Tree id="ClassTree" items="{path: 'Tree>/'}" toggleOpenState="onOpenAnItemOnTree">
<CustomTreeItem>
<FlexBox width="100%" alignItems="Center" justifyContent="SpaceBetween">
<items>
<Label text="{Tree>text}" wrapping="true"/>
<Button icon="sap-icon://display" type="Transparent" press="onClassPressed"/>
</items>
</FlexBox>
</CustomTreeItem>
</Tree>
</mvc:View>
And the Search.Controller
sap.ui.define([
"ch/mindustrie/ZMM_CLASSIFICATION/controller/BaseController"
], function (BaseController) {
"use strict";
return BaseController.extend("ch.mindustrie.ZMM_CLASSIFICATION.controller.Search", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* #memberOf ch.mindustrie.ZMM_CLASSIFICATION.view.Search
*/
onInit: function () {
//Set model for smart filter bar
const oModel = this.getModel("Classification");
this.setModel(oModel);
//Model for classification tree
this.setModel(new sap.ui.model.json.JSONModel(), "Tree");
},
onSearchClass: function () {
const aFilter = this.byId("SelectionFilterBar").getFilters();
const self = this;
//Build the first node
this._readClassification(aFilter)
.then(aData => self.getModel("Tree").setProperty("/", aData));
},
/*
* Read classification from server.
*/
_readClassification: function (aFilter) {
const self = this;
//Build entry node
const oModel = this.getModel("Classification");
const oDateFrom = new sap.ui.model.Filter({
path: "ValidFrom",
operator: sap.ui.model.FilterOperator.LE,
value1: new Date()
});
const oDateUntil = new sap.ui.model.Filter({
path: "ValidFrom",
operator: sap.ui.model.FilterOperator.GE,
value1: new Date()
});
return new Promise((resolve, reject) => {
oModel
.read("/ZMM_C_CLASSIFICATION", {
filters: aFilter.concat([oDateFrom, oDateUntil]),
success: function (oData) {
let aTreeWithNodes = [];
const aTree = oData.results.map(self._buildEntryTree);
aTree.forEach((oNode, index) => {
self._readSubClasses(oNode.internalClass)
.then(aData => {
const oWithNodes = aTree[index];
if (aData.length > 0) {
oWithNodes.nodes = aData;
}
aTreeWithNodes.push(oWithNodes);
if (aTreeWithNodes.length === aTree.length) {
resolve(aTreeWithNodes);
}
});
});
},
error: function (oError) {
reject(oError);
}
});
});
},
_readSubClasses: function (sSubInternalClass) {
const oModel = this.getModel("Classification");
const self = this;
const oUpInteralClass = new sap.ui.model.Filter({
path: "UpInternalClass",
operator: sap.ui.model.FilterOperator.EQ,
value1: sSubInternalClass
});
return new Promise((resolve) => {
oModel
.read("/ZMM_C_CLASSSUB_REL", {
filters: [oUpInteralClass],
success: function (oData) {
const aNewNodes = (oData.results.length > 0 ? oData.results.map(self._prepareDescEntryNodes) : []);
resolve(aNewNodes);
},
error: function () {
resolve([]);
}
});
});
},
/*
* Build the entry node.
*/
_buildEntryTree: function (oData) {
return {
text: oData.ClassType + " " + oData.ClassNum + " " + oData.ClassNumDescr,
classType: oData.ClassType,
classNum: oData.ClassNum,
internalClass: oData.InternalClass,
nodes: []
};
},
onOpenAnItemOnTree: function (oEvent) {
const bExpanded = oEvent.getParameter("expanded");
if (!bExpanded) {
return;
}
const iItemIndex = oEvent.getParameter("itemIndex");
const oItemContext = oEvent.getParameter("itemContext");
const oTree = this.byId("ClassTree");
const oModel = this.getView().getModel("Tree");
const sPath = oItemContext.getPath();
const oCurrentNode = oModel.getProperty(sPath);
this._loadOnDemand(oModel, oCurrentNode, sPath, oTree.getItems()[iItemIndex].getLevel());
},
/*
* Load descendants asynchronously.
*/
_loadOnDemand: function (oModel, oCurrentNode, sPath) {
const aChildNodes = oCurrentNode.nodes.map(oClass => {
return new sap.ui.model.Filter({
path: "InternalClass",
operator: sap.ui.model.FilterOperator.EQ,
value1: oClass.internalClass
});
});
this._readClassification(aChildNodes)
.then(aNewNodes => oModel.setProperty(sPath + "/nodes", aNewNodes));
},
/*
* When the item in the tree is getting pressed.
*/
onClassPressed: function (oEvent) {
const oRow = oEvent.getSource().getBindingContext("Tree").getObject();
this.getRouter().navTo("characteristic", {
internalclassnum: oRow.internalClass
});
}
});
});
I believe the flexibility services only work in the Fiori launchpad, not in the webide sandbox launchpad. Do you get the same issue if you deploy?
Related
I am writing a simple application which aims to display the local data of a json file.
How can I sum the values from a column?
Expected effect:
enter image description here
Below are the files to recreate the case
V_Root.view.xml
<mvc:View controllerName="Nawigacja.ZNavigation.controller.V_Root" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m">
<App id="V_Main">
<pages>
<Page title="Root View">
<content></content>
</Page>
</pages>
</App>
</mvc:View>
V_Source.view.xml
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="Nawigacja.ZNavigation.controller.V_Source"
xmlns:html="http://www.w3.org/1999/xhtml">
<App>
<pages>
<Page title="Soruce View">
<content>
<VBox width="100%" direction="Column" id="__vbox0">
<items>
<Button text="Button 1" width="100px" id="__button2" press="GoToTarget_1"/>
<Button text="Button 2" width="100px" id="__button3" press="GoToTarget_2"/>
</items>
</VBox>
</content>
</Page>
</pages>
</App>
</mvc:View>
V_Target_1.view.xml
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="Nawigacja.ZNavigation.controller.V_Target_1"
xmlns:html="http://www.w3.org/1999/xhtml">
<App>
<pages>
<Page title="Target One" showNavButton="true" navButtonPress="GoOneScreenBack">
<content>
<ScrollContainer vertical="true" focusable="true" height="95%">
<Table id="namiot" items="{path: '/namiot'}">
<columns>
<Column width="12rem">
<Label text="Najem"/>
</Column>
<Column minScreenWidth="tablet" demandPopin="true">
<Label text="Price"/>
</Column>
<Column width="12rem">
<Label text="Total Cost"/>
<footer>
<Text text="Sum:"/>
</footer>
</Column>
</columns>
<ColumnListItem>
<Text text="{ProductType}"></Text>
<Text text="{ProductPrice}"></Text>
<Text text="{ProductTotal}"></Text>
</ColumnListItem>
</Table>
</ScrollContainer>
</content>
</Page>
</pages>
</App>
</mvc:View>
V_Root.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function (Controller) {
"use strict";
return Controller.extend("Nawigacja.ZNavigation.controller.V_Root", {
onInit: function () {
}
});
});
V_Source.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function (Controller) {
"use strict";
return Controller.extend("Nawigacja.ZNavigation.controller.V_Source", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf Nawigacja.ZNavigation.view.V_Source
*/
onInit: function () {
},
GoToTarget_1: function () {
// Now Get the Router Info
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
// Tell the Router to Navigate To Route_Tar_1
oRouter.navTo("Route_Tar_1", {});
},
GoToTarget_2: function () {
// Now Get the Router Info
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
// Tell the Router to Navigate To Route_Tar_2
oRouter.navTo("Route_Tar_2", {});
}
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf Nawigacja.ZNavigation.view.V_Source
*/
// onBeforeRendering: function() {
//
// },
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf Nawigacja.ZNavigation.view.V_Source
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf Nawigacja.ZNavigation.view.V_Source
*/
// onExit: function() {
//
// }
});
});
V_Target_1.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/routing/History",
"Nawigacja/ZNavigation/formatter/Formatter"
], function (Controller, History,formatter) {
"use strict";
return Controller.extend("Nawigacja.ZNavigation.controller.V_Target_1", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf Nawigacja.ZNavigation.view.V_Target_1
*/
onInit: function () {
var oTable = this.getView().byId("namiot");
var oModel = new sap.ui.model.json.JSONModel();
oModel.loadData("model/namiot.json");
oTable.setModel(oModel);
},
GoOneScreenBack: function (Evt) {
var oHistory = History.getInstance();
var sPreviousHash = oHistory.getPreviousHash();
// Go one screen back if you find a Hash
if (sPreviousHash !== undefined) {
window.history.go(-1);
}
// If you do not find a correct Hash, go to the Source screen using default router;
else {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("", true);
}
},
formatter: formatter
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf Nawigacja.ZNavigation.view.V_Target_1
*/
// onBeforeRendering: function() {
//
// },
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf Nawigacja.ZNavigation.view.V_Target_1
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf Nawigacja.ZNavigation.view.V_Target_1
*/
// onExit: function() {
//
// }
});
});
models.js
sap.ui.define([
"sap/ui/model/json/JSONModel",
"sap/ui/Device"
], function (JSONModel, Device) {
"use strict";
return {
createDeviceModel: function () {
var oModel = new JSONModel(Device);
oModel.setDefaultBindingMode("OneWay");
return oModel;
}
};
});
manifest.json
{
"_version": "1.12.0",
"sap.app": {
"id": "Nawigacja.ZNavigation",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"version": "1.40.12"
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"rootView": {
"viewName": "Nawigacja.ZNavigation.view.V_Root",
"type": "XML",
"async": true,
"id": "V_Root"
},
"dependencies": {
"minUI5Version": "1.65.6",
"libs": {
"sap.ui.layout": {},
"sap.ui.core": {},
"sap.m": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "Nawigacja.ZNavigation.i18n.i18n"
}
}
},
"resources": {
"css": [{
"uri": "css/style.css"
}]
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "Nawigacja.ZNavigation.view",
"controlAggregation": "pages",
"controlId": "V_Main",
"clearControlAggregation": false,
"viewLevel": 1
},
"routes": [{
"name": "Route_Source",
"pattern": "",
"target": ["Target_Source"],
"titleTarget": ""
}, {
"name": "Route_Tar_1",
"pattern": "V_Target_1",
"titleTarget": "",
"greedy": false,
"target": ["Target_1"]
}, {
"name": "Route_Tar_2",
"pattern": "V_Target_2",
"titleTarget": "",
"greedy": false,
"target": ["Target_2"]
}],
"targets": {
"Target_1": {
"viewType": "XML",
"transition": "slide",
"clearAggregation": true,
"viewName": "V_Target_1",
"viewLevel": 2
},
"Target_2": {
"viewType": "XML",
"transition": "slide",
"clearAggregation":"true",
"viewName": "V_Target_2",
"viewLevel": 2
},
"Target_Source": {
"viewType": "XML",
"clearAggregation":"true",
"viewName": "V_Source",
"viewLevel": 1
}
}
}
}
}
namiot.json
{
"namiot":[
{
"ProductName": "Product1",
"ProductType": "Type1",
"ProductPrice": "25",
"ProductTotal": "5000",
"Productadd": "",
"ProductaddPr":""
},
{
"ProductName": "Product2",
"ProductType": "Type2",
"ProductPrice": "25",
"ProductTotal": "5000",
"Productadd": "",
"ProductaddPr":""
}
]
}
there are probably x options to realize this.
You could use the method attachUpdateFinished which will be called when the Tablebinding get's updated (the table receives the Items). Then you can summarize the values and store it in an JSONModel and bind it to the footertext. When the binding (the items) change the sum would be updated.
oTable.attachUpdateFinished(function (oEvt) {
var iSum = 0;
var aItems = oEvt.getSource().getItems();
for (var i = 0; i < aItems.length; i++) {
var oItem = aItems[i].getBindingContext().getObject();
iSum = iSum + parseFloat(oItem.ProductTotal);
}
//Do whatever you want with the iSum
});
Here's an example how it could be realized.
Code Sample
I'm trying to run a SAPUI5 application. I created a view with a view piecharts and add them in index.html to the body by referring to its ID content
<script>
var app = new sap.m.App();
var page = sap.ui.view({
viewName: "...",
type: sap.ui.core.mvc.ViewType.XML
});
app.addPage(page);
app.placeAt("content");
</script>
My problem now is that the Component.js isn't called. So I have no chance of using Routing since the Router isn't initialized.
I tried some different ways, like this:
<script>
sap.ui.getCore().attachInit(function() {
new sap.m.Shell({
app: new sap.ui.core.ComponentContainer({
height: "100%",
name: "vizFrame.gettingStarted.demoVizFrame"
})
}).placeAt("content");
});
</script>
After this approach, the Component.js got triggered, but the page is empty. The content of my root view isn't displayed.
manifest.json
{
"_version": "1.12.0",
"sap.app": {
"id": "vizFrame.gettingStarted.demoVizFrame",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"version": "1.40.12"
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"rootView": {
"viewName": "vizFrame.gettingStarted.demoVizFrame.view.VizChart",
"type": "XML"
},
"dependencies": {
"minUI5Version": "1.60.1",
"libs": {
"sap.ui.layout": {},
"sap.ui.core": {},
"sap.m": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "vizFrame.gettingStarted.demoVizFrame.i18n.i18n"
}
}
},
"resources": {
"css": [
{
"uri": "css/style.css"
}
]
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "vizFrame.gettingStarted.demoVizFrame.view",
"controlAggregation": "pages",
"controlId": "app",
"clearControlAggregation": false
},
"routes": [
{
"name": "RouteVizChart",
"pattern": "RouteVizChart",
"target": ["TargetVizChart"]
},
{
"pattern": "detail",
"name": "detail",
"target": "detail"
}
],
"targets": {
"TargetVizChart": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewId": "VizChart",
"viewName": "VizChart"
},
"testView": {
"viewType": "XML",
"viewName": "testView",
"viewId": "testView",
"viewLevel": 2
},
"detail": {
"viewType": "XML",
"viewName": "detailView"
}
}
}
}
}
Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"vizFrame/gettingStarted/demoVizFrame/model/models"
], function (UIComponent, Device, models) {
"use strict";
return UIComponent.extend("vizFrame.gettingStarted.demoVizFrame.Component", {
metadata: {
manifest: "json"
},
init: function () {
UIComponent.prototype.init.apply(this, arguments);
this.getRouter().initialize();
this.setModel(models.createDeviceModel(), "device");
}
});
});
VizChart.view.xml
This is the root view where only the page title gets displayed:
<mvc:View controllerName="vizFrame.gettingStarted.demoVizFrame.controller.VizChart"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:viz="sap.viz.ui5.controls"
xmlns:chart="sap.suite.ui.commons"
xmlns:l="sap.ui.layout"
>
<Page title="Übersicht">
<l:VerticalLayout
width="100%"
class="gridWrapper">
<l:Grid containerQuery="true">
<viz:VizFrame xmlns="sap.viz" id="l100" width="100%" selectData="onDataClick" />
<viz:VizFrame xmlns="sap.viz" id="l300" width="100%" selectData="onDataClick" />
<viz:VizFrame xmlns="sap.viz" id="l400" width="100%" selectData="onDataClick" />
<viz:VizFrame xmlns="sap.viz" id="l430" width="100%" selectData="onDataClick" />
<viz:VizFrame xmlns="sap.viz" id="l499" width="100%" selectData="onDataClick" />
</l:Grid>
</l:VerticalLayout>
</Page>
</mvc:View>
The topic App Initialization: What Happens When an App Is Started? explains which files are loaded in which order generally. In most cases, a Component is loaded with its descriptor (manifest.json) once the ComponentContainer is instantiated.
About the issue with the page being empty, see my other answer in stackoverflow.com/a/50951902.
In your case, the root view is missing a root control (e.g. sap.m.App). So it should be:
<!-- Root view -->
<mvc:View xmlns:mvc="sap.ui.core.mvc">
<App id="app" xmlns="sap.m">
<!-- Leave this block empty if Router should be used. Otherwise, continue.. -->
<Page title="Übersicht">
<!-- content -->
</Page>
</App>
</mvc:View>
In your 1st snippet, you could see the page content because there was already sap.m.App.
ComponentContainer loads component.js file from index.html
component.js loads component descriptor(manifest.json - application descriptor file to declare dependencies) which is referenced in the file.
component.js create root view, default and resource(i18n) model.
index.html
sap.ui.getCore().attachInit(function () {
new sap.ui.core.ComponentContainer({
name: "ABC.AppName"
}).placeAt("content");
});
component.js
sap.ui.core.UIComponent.extend("ABC.AppName.Component", {
metadata : {
includes: [jQuery.device.is.phone ? "assets/css/mStyle.css" : "assets/css/dStyle.css"]
},
createContent: function () {
// create root view
var oView = sap.ui.view({
id: "app",
viewName: "ABC.AppName.ViewFolder.App",
type: "JS",
viewData: { component: this },
});
i18nGlobal = new sap.ui.model.resource.ResourceModel({
bundleUrl: "i18n/i18n.properties",
bundleLocale: "de"
});
oView.setModel(i18nGlobal, "i18n");
sap.ui.getCore().setModel(i18nGlobal, "i18n");
return oView;
}
});
Probably I'm missing something basic here. Scenario goes like this:
Within the root view controller of my application (App.controller.js, onInit function) I'm performing a series of read operations on a named model in order to have additional data ready mainly for use by formatters in subsequent views. What I have done is using promises to make sure that data will be ready.
Problem is that application occasionally crashes when the initial view with pattern "" (Worklist.view.xml in my case) is loaded after the root view (App.view.xml) because the formatter function can't find the data expected. Added a few break-points and observed the debugger:
stopping at the first read function
stopping at the promises condition at the end
stopping (and throwing error) at the formatter function at the subsequent view
stopping in the success function of oData read (resolve)
page freezes afterwards as result of the exception thrown above
"Normal" flow, when application doesn't crash goes like this:
stopping at the first read function
stopping at the promises condition at the end
stopping in the success function of oData read (resolve)
stopping at the readyToGo function (it's there just for temporary debugging)
stopping at the formatter function at the subsequent view
page display, everything in order
Judging by the above, I have reached the conclusion that my promises do not work. Any ideas?
sap.ui.define([
"kristal/apps/agreements/controller/BaseController",
"sap/ui/model/json/JSONModel"
], function (BaseController, JSONModel) {
"use strict";
/* PROMISE VARIABLES */
var oModelTypeDataDeferred = jQuery.Deferred();
var oModelStatusDataDeferred = jQuery.Deferred();
var oModelRoleDataDeferred = jQuery.Deferred();
var oModelRefDataDeferred = jQuery.Deferred();
var oModelExtOrgDataDeferred = jQuery.Deferred();
var oModelInvolvementDataDeferred = jQuery.Deferred();
return BaseController.extend("kristal.apps.agreements.controller.App", {
onInit: function () {
/* CALLED AFTER PROMISES HAVE BEEN FULFILLED */
var readyToGo = function() {
jQuery.sap.log.error("Ready!");
};
var oViewModel;
var fnSetAppNotBusy;
var iOriginalBusyDelay = this.getView().getBusyIndicatorDelay();
var oViewModel = new JSONModel({
busy: true,
delay: 0
});
this.setModel(oViewModel, "appView");
fnSetAppNotBusy = function() {
oViewModel.setProperty("/busy", false);
oViewModel.setProperty("/delay", iOriginalBusyDelay);
};
this.getOwnerComponent().getModel().metadataLoaded().then(fnSetAppNotBusy);
var oModel = this.getOwnerComponent().getModel("f4");
/* oData READ OPS */
oModel.metadataLoaded().then(function(){
// Initialize additional data
var sPath = "/Agreement_TypesSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelTypeDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
sPath = "/Agreement_StatusesSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelStatusDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
sPath = "/Role_TypesSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelRoleDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
sPath = "/Reference_TypesSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelRefDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
sPath = "/External_OrganizationsSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelRefDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
sPath = "/Involvement_TypesSet";
oModel.read(sPath, {
success: function(oData, oResponse) {
oModelInvolvementDataDeferred.resolve();
},
error: function(oError) {
jQuery.sap.log.error("Error", oError);
}
});
/* IF ALL PROMISES FULFILLED, PROCEED */
jQuery.when(oModelTypeDataDeferred, oModelStatusDataDeferred, oModelRoleDataDeferred, oModelRefDataDeferred, oModelExtOrgDataDeferred, oModelInvolvementDataDeferred)
.done().then(jQuery.proxy(readyToGo, this));
},
// ...
});
});
manifest.json:
{
"_version": "1.4.0",
"sap.app": {
"id": "kristal.apps.agreements",
"type": "application",
"i18n": "i18n/i18n.properties",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "/DEV/sap/opu/odata/SAP/ZCONTRACTS_SRV/",
"type": "OData",
"settings": {
"odataVersion": "2.0",
"localUri": "localService/metadata.xml"
}
}
},
"sourceTemplate": {
"id": "sap.ui.ui5-template-plugin.1worklist",
"version": "1.38.3"
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "sap-icon://task",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes": [
"sap_hcb",
"sap_bluecrystal"
]
},
"sap.ui5": {
"rootView": {
"viewName": "kristal.apps.agreements.view.App",
"type": "XML",
"id": "app",
"async": true
},
"dependencies": {
"minUI5Version": "1.38.0",
"libs": {
"sap.ui.core": {
"minVersion": "1.38.0"
},
"sap.m": {
"minVersion": "1.38.0"
},
"sap.ushell": {
"minVersion": "1.38.0"
},
"sap.collaboration": {
"minVersion": "1.38",
"lazy": true
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "kristal.apps.agreements.i18n.i18n"
}
},
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"defaultBindingMode": "TwoWay"
}
},
"f4": {
"dataSource": "mainService",
"preload": true,
"settings": {
"metadataUrlParams": {
"sap-documentation": "heading"
},
"defaultBindingMode": "TwoWay"
}
}
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "kristal.apps.agreements.view",
"controlId": "app",
"controlAggregation": "pages",
"bypassed": {
"target": [
"notFound"
]
},
"async": true
},
"routes": [{
"pattern": "",
"name": "worklist",
"target": [
"worklist"
]
}, {
"pattern": "AgreementsSet/{objectId}",
"name": "object",
"target": [
"object"
]
}],
"targets": {
"worklist": {
"viewName": "Worklist",
"viewId": "worklist",
"viewLevel": 1
},
"object": {
"viewName": "Object",
"viewId": "object",
"viewLevel": 2
},
"objectNotFound": {
"viewName": "ObjectNotFound",
"viewId": "objectNotFound"
},
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
}
},
"sap.platform.abap": {
"uri": "/sap/bc/ui5_ui5/sap/zctr_contr_mnt/webapp",
"_version": "1.1.0"
}
}
Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"kristal/apps/agreements/model/models",
"kristal/apps/agreements/controller/ErrorHandler"
], function(UIComponent, Device, models, ErrorHandler) {
"use strict";
return UIComponent.extend("kristal.apps.agreements.Component", {
metadata: {
manifest: "json"
},
init: function() {
UIComponent.prototype.init.apply(this, arguments);
this._oErrorHandler = new ErrorHandler(this);
this.setModel(models.createDeviceModel(), "device");
this.setModel(models.createFLPModel(), "FLP");
this.getRouter().initialize();
},
destroy: function() {
this._oErrorHandler.destroy();
// call the base component's destroy function
UIComponent.prototype.destroy.apply(this, arguments);
},
getContentDensityClass: function() {
if (this._sContentDensityClass === undefined) {
// check whether FLP has already set the content density class; do nothing in this case
if (jQuery(document.body).hasClass("sapUiSizeCozy") || jQuery(document.body).hasClass("sapUiSizeCompact")) {
this._sContentDensityClass = "";
} else if (!Device.support.touch) { // apply "compact" mode if touch is not supported
this._sContentDensityClass = "sapUiSizeCompact";
} else {
// "cozy" in case of touch support; default for most sap.m controls, but needed for desktop-first controls like sap.ui.table.Table
this._sContentDensityClass = "sapUiSizeCozy";
}
}
return this._sContentDensityClass;
}
});
});
It was all about moving functionality from the root view to component.js and putting the router initialization into the promise conditions, thank you experts
Tring to add an object route to the main route(table page), but return this.getRouter is not a function error.
In main Controller:
onPress : function(oEvent) {
this._showObject(oEvent.getSource());
},
_showObject : function (oItem) {
this.getRouter().navTo("object", {
objectId: oItem.getBindingContext().getProperty("task_id")
});
},
In Component.js(I've checked , it's already loaded, no error produced)
sap.ui.define(["sap/ui/core/UIComponent"],
function (UIComponent) {
"use strict";
return UIComponent.extend("cts.mobile.Component", {
metadata : {
rootView : "cts.mobile.view.TaskTest",
routing : {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "cts.mobile.view",
"controlId": "taskapp", //in task.view.xml
"controlAggregation": "pages",
"async": true
},
"routes": [
{
"pattern": "",
"name": "task",
"target": "task"
},
{
"pattern": "ProductCollection/{objectId}",
"name": "object",
"target": "object"
}
],
"targets": {
"worklist": {
"viewName": "TaskTest",
"viewId": "TaskTest",
"viewLevel": 1
},
"object": {
"viewName": "Object",
"viewId": "object",
"viewLevel": 2
}
}
}
},
init : function () {
UIComponent.prototype.init.apply(this, arguments);
// Parse the current url and display the targets of the route that matches the hash
this.getRouter().initialize();
}
});
}
);
this value in _showObject:
f {mEventRegistry: Object, oView: f}
How to fix this error?
Try
return this.getOwnerComponent().getRouter();
Instead of what you now have in the getRouter function.
Tried
onPress : function(oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("object", {
objectId:
oEvent.getSource().getBindingContext().getProperty("task_id")
});
And it's working.
ref: https://openui5.hana.ondemand.com/#docs/guide/e5200ee755f344c8aef8efcbab3308fb.html
Thanks to #hdereli
I forgot to include the helper : BaseController.js
getRouter : function () {
return sap.ui.core.UIComponent.getRouterFor(this)
},
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"RoutNav/model/models"
], function (UIComponent, Device, models) {
"use strict";
return UIComponent.extend("RoutNav.Component", {
metadata: {
manifest: "json",
rootView: "RoutNav.view.View1",
routes: [
{
pattern: "",
name: "Master",
view: "Master",
targetAggregation: "masterPages",
targetControl: "idAppControl",
subroutes: [
{
pattern: "tab:",
name: "Detail",
view: "Detail"
}
]
}
]
},
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* #public
* #override
*/
init: function () {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// set the device model
this.setModel(models.createDeviceModel(), "device");
}
});
});
I have tried this, in master.view.xml page I have just given a title. It's not working.
you missing config code
routing : {
config : {
routerClass : Application_Name.MyRouter,
viewType : "XML",
viewPath : "Application_Name.view",
targetAggregation : "detailPages",
transition: "slide",
clearTarget : false
},
routes : [
{
pattern : "",
name : "Master",
view : "Master",
targetAggregation : "masterPages",
targetControl : "idAppControl",
subroutes : [
{
pattern : "tab:",
name : "Detail",
view : "Detail"
}
]
}
]
}
Steps to navigate master to detail..using JSON
Master.view.xml
<List id="list" growing="true" mode="SingleSelectMaster"
growingThreshold="100" growingScrollToLoad="true" select="onSelect"
noDataText="No Data">
</List>
Master.controller.js
navBack: function(){
var oRouter = new sap.ui.core.UIComponent.getRouterFor(this);
oRouter.myNavBack("S1");
},
onInit: function(){
this.data = new sap.ui.model.json.JSONModel("Model/data.json");
sap.ui.getCore().setModel(this.data);
var list = this.getView().byId("list");
list.bindItems("/employees", new sap.m.StandardListItem({
title: "{fname}"
}));
list.setModel(this.data);
},
onSelect: function(evt){
var index = evt.getParameter("listItem").getBindingContext().getPath();
index.charAt(index.length-1)-2;
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("Detail", {
key : evt.getParameter("listItem").getTitle()
});
}
Detail.view.xml
<Table onSelect="onpress" id="table" class="sapUiSmallMarginTop">
<headerToolbar>
<Toolbar>
<Title level="H2" text="Personal Details" />
<ToolbarSpacer />
<Button press="onTableSettings" icon="sap-icon://drop-down-list" />
</Toolbar>
</headerToolbar>
<columns>
<Column>
<Text text="Last_Name" key="name"/>
</Column>
<Column>
<Text text="Mobile" />
</Column>
<Column>
<Text text="Age" key="Age" />
</Column>
</columns>
</Table>
Detail.controller.js
onInit : function() {
this.getRouter().attachRoutePatternMatched(this.onRouteMatched, this);
},
onRouteMatched : function(oEvent) {
// debugger;
this.data = new sap.ui.model.json.JSONModel("Model/data.json");
this.getView().setModel(this.data);
var dataKey = oEvent.getParameter("arguments").key;
this.getView().getModel().attachRequestCompleted(function() {
var data2 = this.getView().getModel().getData().employees;
var otable = this.getView().byId("table");
for (var i = 0; i < data2.length; i++) {
if (data2[i].fname === dataKey) {
otable.bindItems("/employees", new sap.m.ColumnListItem({
cells : [ new sap.m.Text({
text : "{lname}",
}), new sap.m.Text({
text : "{mobile}"
}), new sap.m.Text({
text : "{age}"
}) ]
}));
}
otable.setModel(this.data);
}
}, this);
},
getRouter : function() {
return sap.ui.core.UIComponent.getRouterFor(this);
},
Component.js
routing : {
config : {
routerClass : Application_Name.MyRouter,
viewType : "XML",
viewPath : "Application_Name.view",
targetAggregation : "detailPages",
transition: "slide",
clearTarget : false
},
routes : [
{
pattern : "",
name : "main",
view : "Master",
targetAggregation : "masterPages",
targetControl : "idAppControl",
subroutes : [
{
pattern:"Detail/{key}",
name: "Detail",
view: "Detail",
targetAggregation: "detailPages",
}
]
}
]
}
MyRouter.js
Just find and replace you Application_Name in MyRouter.js file
jQuery.sap.require("sap.m.routing.RouteMatchedHandler");
jQuery.sap.require("sap.ui.core.routing.Router");
jQuery.sap.declare("Application_Name.MyRouter");
sap.ui.core.routing.Router.extend("Application_Name.MyRouter", {
constructor : function() {
sap.ui.core.routing.Router.apply(this, arguments);
this._oRouteMatchedHandler = new sap.m.routing.RouteMatchedHandler(this);
},
myNavBack : function(sRoute, mData) {
var oHistory = sap.ui.core.routing.History.getInstance();
var sPreviousHash = oHistory.getPreviousHash();
//The history contains a previous entry
if (sPreviousHash !== undefined) {
window.history.go(-1);
} else {
var bReplace = true; // otherwise we go backwards with a forward history
this.navTo(sRoute, mData, bReplace);
}
},
myNavToWithoutHash : function (oOptions) {
var oSplitApp = this._findSplitApp(oOptions.currentView);
// Load view, add it to the page aggregation, and navigate to it
var oView = this.getView(oOptions.targetViewName, oOptions.targetViewType);
oSplitApp.addPage(oView, oOptions.isMaster);
oSplitApp.to(oView.getId(), oOptions.transition || "show", oOptions.data);
},
backWithoutHash : function (oCurrentView, bIsMaster) {
var sBackMethod = bIsMaster ? "backMaster" : "backDetail";
this._findSplitApp(oCurrentView)[sBackMethod]();
},
destroy : function() {
sap.ui.core.routing.Router.prototype.destroy.apply(this, arguments);
this._oRouteMatchedHandler.destroy();
},
_findSplitApp : function(oControl) {
var sAncestorControlName = "idAppControl";
if (oControl instanceof sap.ui.core.mvc.View && oControl.byId(sAncestorControlName)) {
return oControl.byId(sAncestorControlName);
}
return oControl.getParent() ? this._findSplitApp(oControl.getParent(), sAncestorControlName) : null;
}
});
Don't forget to add MyRouter.js file
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewPath": "mc7prod.view",
"viewType": "XML",
"controlAggregation": "pages",
"clearTarget": "false"
},
"routes": [
{
"pattern": "",
"name": "Index",
"view": "Index",
"targetControl": "appId"
},
{
"pattern": "split",
"name": "AppMasterDetail",
"view": "AppMasterDetail",
"targetControl": "appId",
"subroutes": [
{
"pattern": "master/{entity}",
"name": "master",
"view": "TS_Master",
"targetAggregation": "masterPages",
"preservePageInSpplitContainer": true,
"targetControl": "idAppMaDeControl",
"subroutes": [
{
"pattern": "detail/{entity}",
"name": "detail",
"view": "TS_Detail",
"targetAggregation": "detailPages"
}
]}]
},