Routing with parameters does not work - sapui5

I am following the tutorial here and I got stuck on routing with parameters.
The example app did not run on my local use, I therefore change it to make use of local data. However, I get the error "Uncaught Error: Invalid value "Invoices/1" for segment "{invoicePath}"" when I click on an element in the invoices list. It should open up a new Detail page and display the product name and the amount.
Here is my routing manifest:
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "sap.ui.demo.wt.view",
"controlId": "app",
"controlAggregation": "pages"
},
"routes": [
{
"pattern": "",
"name": "overview",
"target": "overview"
},
{
"pattern": "detail/{invoicePath}",
"name": "detail",
"target": "detail"
}
],
"targets": {
"overview": {
"viewName": "Overview"
},
"detail": {
"viewName": "Detail"
}
}
}
Invoices.json example data:
{
"Invoices": [
{
"ProductName": "Pineapple",
"Quantity": 21,
"ExtendedPrice": 87.2000,
"ShipperName": "Fun Inc.",
"ShippedDate": "2015-04-01T00:00:00",
"Status": "A"
}
]
}
InvoiceList.controller.js. Where I populate the Invoices list and call the view change.
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"sap/ui/demo/wt/model/formatter",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator"
], function (Controller, JSONModel, formatter, Filter, FilterOperator) {
"use strict";
return Controller.extend("sap.ui.demo.wt.controller.InvoiceList", {
onPress: function (oEvent) {
var oItem = oEvent.getSource();
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("detail", {
invoicePath: oItem.getBindingContext("invoice").getPath().substr(1)
});
}
});
});

The error message is raised by the the router library. The route is defined as detail/{invoicePath} and you pass Invoice/1 as parameter, which is not allowed as the parameter contains the slash which is considered as URL segment separator.
However, you mentioned that you could not run the example locally and did some adoptions. The path looks like you are using a JSONModel now. This means you need to adopt several parts in your example as well.
InvoiceList controller:
oItem.getBindingContext("invoice").getPath().substr(10)
The binding context should be /Invoices/1 and you want to pass the only the index. Therefore you need to cut off /Invoices/.
Detail controller:
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/Invoices/"+ oEvent.getParameter("arguments").invoicePath,
model: "invoice"
});
}
This will bind your view to /Invoices/1 in the corresponding model.

#matbtt 's answer is right.
Another solution is to excaping the PATH. No substr and special offset is needed.
encodeURIComponent(oItem.getBindingContext("invoice").getPath())
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: decodeURIComponent(oEvent.getParameter("arguments").invoicePath),
model: "invoice"
});
}
both JSON Model and OData would work well.

Related

How to resolve the error: "The target [...] no viewName defined"?

I'm pretty new in SAPUI5 and started to learn the subject on Routing and navigation as you can see in their documentation:
https://ui5.sap.com/#/topic/e5200ee755f344c8aef8efcbab3308fb
Now I did all the changes like they do in their documentation but I have an error that I don't understand why it is being thrown:
The target overview no viewName defined. - EventProvider sap.m.routing.Target
Here is the manifest:
{
"...": "...",
"sap.ui5": {
"_version": "1.1.0",
"resources": {
"css": [
{
"uri": "css/style.css"
}
]
},
"dependencies": {
"minUI5Version": "1.60",
"libs": {
"sap.m": {}
}
},
"...": "...",
"rootView": {
"viewName": "sap.ui.startApp.view.App",
"type": "XML",
"async": true,
"id": "app"
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"type": "View",
"viewType": "XML",
"path": "sap.ui.startApp.view",
"controlId": "app",
"controlAggregation": "pages"
},
"routes": [
{
"pattern": "",
"name": "overview",
"target": "overview"
},
{
"pattern": "detail",
"name": "detail",
"target": "detail"
}
],
"targets": {
"overview": {
"id": "overview",
"name": "Overview"
},
"detail": {
"id": "detail",
"name": "Detail"
}
}
}
}
}
Solution
Either add "async": true to /sap.ui5/routing/config/ in manifest.json
Or preferably add "sap.ui.core.IAsyncContentCreation" to the application's UIComponent:
sap.ui.define([
"sap/ui/core/UIComponent",
// ...
], function(UIComponent/*,...*/) {
"use strict";
return UIComponent.extend("my.Component", {
metadata: {
interfaces: [
"sap.ui.core.IAsyncContentCreation", // Available since 1.89.0
],
manifest: "json",
},
// ...
});
});
Analysis
The /sap.ui5/routing/targets properties viewName, viewId, viewPath, and viewLevel are regarded as legacy syntax since UI5 1.58.0 (as part of the initiative to support routing with nested components) and thus it is correct to declare name, id, path, and level respectively instead of viewName, etc.. *
The error "[...] no viewName defined" actually comes from sap/ui/core/routing/sync/Target.js. The /sync part indicates that the views are created synchronously which should be avoided because it's blocking the UI thread of the browser during the routing process. Instead, targets should be created asynchronously which, however, requires an additional flag as shown above.
If the targets are created asynchronously, the error will be gone.
* In case existing apps still need to migrate to the new name, id, path, and level syntax:
Add:
"type": "View" to /sap.ui5/routing/config/ (Cf. commit: ef476f1)
"minUI5Version": "1.98.0" or higher to /sap.ui5/dependencies/ (Cf. commit: 867496d)
The property /sap.ui5/rootView/viewName is not affected.
Related discussion: https://github.com/SAP/openui5/issues/3417

Problem with Routing from one view to the other in SAPUI5

I have been trying to route from View1 to View2. My code is the same as what is given in the SAPUI5 SDK, with some minor changes according to my project. I tried multiple solutions given on the internet but none of them worked.
View1.controller.js
onNav: function (oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("View2");
//return this.getOwnerComponent().getRouter().navTo("View2");
},
manifest.json
"routes": [
{
"pattern": "",
"name": "View1",
"target": "View1"
},
{
"pattern": "View2",
"name": "View2",
"target": "View2"
}
],
"targets": {
"View1": {
"viewId": "View1",
"viewName": "View1"
},
"View2": {
"viewId": "View2",
"viewName":"View2"
}
}
The error shown is:
sap.ui.core.UIComponent.getRouterFor is not a function
Double check if you have the following:
1) Router class declared in the routing configuration of the manifest.json file:
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
},
"routes": [
]
2) Router was initialize. This is almost always done inside Component.js:
init: function () {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// enable routing
this.getRouter().initialize();
},
3) Check if this inside your onNav method is your controller.
For that, you can add a breakpoint inside it, open the browser console use the following call:
this.getMetadata()
Use the sap.ui.core.UIComponent.getRouterFor(this) inside a controller onInit method. You should get the reference for the router at that point.

How not to display DetailNoObjectsAvailable view via mobile?

I've created a sapui5 project from master-detail template. What I want to achieve is showing an empty list (which is in the master view) via mobile without navigating to DetailNoObjectsAvailable view. I just can't find where DetailNoObjectsAvailable view is called from.
The reason why I want to achieve this is that when there are no objects in the list in the beginning and then an object is added to odata source, I see nothing but 'No objects available' and I can not navigate to the master list to refresh it manually. I have to refresh the whole page in fiori client browser to see it. When the list is not empty in the beginning and I add an object to odata source, the list refreshes automatically and I face no problems seeing it.
I haven't changed anything in the routing definition:
"routes": [
{
"pattern": "",
"name": "master",
"target": [
"object",
"master"
]
}, {
"pattern": "visitors_view/{objectId}",
"name": "object",
"target": [
"master",
"object"
]
}
],
"targets": {
"master": {
"viewName": "Master",
"viewLevel": 1,
"viewId": "master",
"controlAggregation": "masterPages"
},
"object": {
"viewName": "Detail",
"viewId": "detail",
"viewLevel": 2
},
"detailObjectNotFound": {
"viewName": "DetailObjectNotFound",
"viewId": "detailObjectNotFound"
},
"detailNoObjectsAvailable": {
"viewName": "DetailNoObjectsAvailable",
"viewId": "detailNoObjectsAvailable"
},
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
The mechanism routing to the notFoundobject is found in manifest.json:
manifest.json: bypassed + target in routing config
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"viewType": "XML",
"viewPath": "test.test.view",
"controlId": "layout",
"controlAggregation": "beginColumnPages",
"bypassed": {
-------> "target": "notFound" <--------
},
"async": true
},
"targets": {
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
View in webapp/view:
<mvc:View
controllerName="test.test.controller.NotFound"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<MessagePage
id="page"
title="{i18n>notFoundTitle}"
text="{i18n>notFoundText}"
icon="sap-icon://document">
</MessagePage>
</mvc:View>
That is what you are looking for right?
Maybe your problem is solved by changing from "target": "notFound" to "target": "master" within the bypassed section in manifest.json.
UPDATE: Detail.controller.js
_onBindingChange : function () {
var oView = this.getView(),
oElementBinding = oView.getElementBinding();
// No data for the binding
if (!oElementBinding.getBoundContext()) {
this.getRouter().getTargets().display("detailObjectNotFound"); <------------
// if object could not be found, the selection in the master list
// does not make sense anymore.
this.getOwnerComponent().oListSelector.clearMasterListSelection();
return;
}
Solved the problem by commenting some lines in ListSelector.js. They caused an error in the console, which in its turn was the cause of my problem:
constructor : function () {
this._oWhenListHasBeenSet = new Promise(function (fnResolveListHasBeenSet) {
this._fnResolveListHasBeenSet = fnResolveListHasBeenSet;
}.bind(this));
this.oWhenListLoadingIsDone = new Promise(function (fnResolve, fnReject) {
this._oWhenListHasBeenSet
.then(function (oList) {
oList.getBinding("items").attachEventOnce("dataReceived",
function (oData) {
if (!oData.getParameter("data")) {
fnReject({
list : oList,
error : true
});
}
var oFirstListItem = this.getFirstListItem();
if (oFirstListItem) {
fnResolve({
list : oList,
firstListitem : oFirstListItem
});
}
// else {
// fnReject({
// list : oList,
// error : false
// });
//}
}.bind(this)
);
}.bind(this));
}.bind(this));
},

Controller "Controller name" couldn't be instantiated SAPUI

I'm getting an error while I'm trying to navigate to another page in the sapui5 application. I'm trying to figure out what I am doing wrong. The directory Structure of my application is as below:
In manifest file
"routes": [{
"pattern": "",
"name": "appHome",
"target": "home"
}
, {
"pattern": "Course/{courseId}",
"name": "course",
"target": "course"
}],
"targets": {
"home": {
"viewName": "Home",
"viewLevel" : 1
},
"notFound": {
"viewName": "NotFound",
"transition": "show"
} ,
"course": {
"viewPath": "dlesDLESdashboard.view.Course",
"viewName": "CourseDetail",
"viewLevel" : 2
}
}
},
When I click on my navigate button I am triggering this event by default I'm giving static ID later I'll change.
handleCourseClick : function(oEvent){
this.getRouter().navTo("course",{courseId :"222222"});},
I'm not sure this information is good enough to find out my error or not.
The Error is that when I click on the tile I'm getting this error instead of navigating to my course detail page.
Check your controller, there might be something wrong with your view and controller connection. check namespace, name of controller etc and verify it.
sap.ui.define([
"sap/ui/model/json/JSONModel",
"locations/util/Utils",
"locations/controller/BaseController"
], function(BaseController, JSONModel, Utils) {
I declared these in the wrong order, and caused the same problem.

How to get the value of a parameter from the url to pass on to the event handler

I have a business requirement where in i am developing a sapui5 employee leave application deployed in HCP using cloud based sap webide tool.
Two types of employees will access this application using two different url with parameters either 'IT' or 'BPO':
https://webidetesting453789-inf98834.dispatcher.int.sap.hana.ondemand.com/webapp/index.html#/IT
https://webidetesting453789-inf98834.dispatcher.int.sap.hana.ondemand.com/webapp/index.html#/BPO
Backend : I have developed a REST service which is giving me the employee details in jSON format when i do a GET request using the following url either for IT or BPO specific employees like:
/irc.com/ircit/empleave/rest/empleave/item/requestor/IT
or
/irc.com/ircit/empleave/rest/empleave/item/requestor/BPO
View: I have used a xml view MyRequest which will shows me requests from either IT or BPO in a tabular format.
And a Create view (XML view) with drop downs which is populated from a 'valueHelp' model and the page title etc changes based on the parameter which is either 'IT' or 'BPO'.
I have a create button in the footer of 'MyRequest' view which will call the Create view.
Requirement:
when i access the link
https://webidetesting453789-inf98834.dispatcher.int.sap.hana.ondemand.com/webapp/index.html#/IT
I am getting all the requests for IT employees and same for BPO in the MyRequest view
But my requirement is in the same MyRequest view when i click the create button it should fetch the url parameter i.e either IT or BPO and navigate to the Create view with that url parameter so that i can set the valuehelp model and change the page title and name of ui components accordingly.
RouterConfiguration.
"routes": [{
"pattern": "",
"name": "myRequests",
"target": "myRequests"
}, {
"pattern": "IT",
"name": "IT",
"target": "myRequests"
}, {
"pattern": "BPO",
"name": "BPO",
"target": "myRequests"
}, {
"pattern": "create",
"name": "Create",
"target": "create"
},
{
"pattern": "/id={id}",
"name": "Display",
"target": "display"
}
],
"targets": {
"myRequests": {
"viewName": "MyRequests",
"viewId": "myRequests",
"viewLevel": 1
},
"display": {
"viewName": "Display",
"viewId": "display",
"viewLevel": 2
},
"create": {
"viewName": "Create",
"viewId": "create",
"viewLevel": 2
},
"copy": {
"viewName": "Create",
"viewId": "create",
"viewLevel": 2
},
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
}
},
MyRequest controller
onInit: function() {
var oComponent = this.getOwnerComponent();
this._router = oComponent.getRouter();
this._router.getRoute("IT").attachPatternMatched(this._routePatternMatchedForIT, this);
this._router.getRoute("BPO").attachPatternMatched(this._routePatternMatchedForBPO, this);
},
_routePatternMatchedForIT: function (oEvent) {
console.log('Shout for IT');
var sRouteName = oEvent.getParameter('name');
// Call the service : /irc.com/ircit/empleave/rest/empleave/item/requestor/IT
},
_routePatternMatchedForBPO: function (oEvent) {
console.log('Shout for BPO');
// Call the service : /irc.com/ircit/empleave/rest/empleave/item/requestor/BPO
var sRouteName = oEvent.getParameter('name');
}
onCreatePress: function(oEvent) {
//var sRouteName = this.getViewName();
//alert(sRouteName);
/*var sRouteName = oEvent.getParameter("name");
if (sRouteName === "IT") {
this.getRouter().navTo("create/IT");
}
if (sRouteName === "BPO"){
this.getRouter().navTo("create/BPO");
}*/
this.getRouter().navTo("Create");
},
MyRequest View for create button
Button id="btnCreate" text="{i18n>btnOpenCreateView}" press="onCreatePress" type="Accept" visible="true"/>
Create Controller
onRouteMatched: function(oEvent) {
var sRouteName = oEvent.getParameter("name");
//alert(sRouteName);
if (sRouteName === "create") {
this.routeName = sRouteName;
this._loadDataDC();
}
Please suggest me how to modify the create controller and MyRequest controller so that i get the value IT or BPO from the url
https://webidetesting453789-inf98834.dispatcher.int.sap.hana.ondemand.com/webapp/index.html#/IT
or
https://webidetesting453789-inf98834.dispatcher.int.sap.hana.ondemand.com/webapp/index.html#/BPO
and pass it on with the event onCreatePress() when create button is pressed so that i can navigate to the Create view and load the corresponding valueHelp model along with setting the page title and other ui components .
Use the below code to fetch the hash :
Use HashChanger:
var oHashChanger = new sap.ui.core.routing.HashChanger();
var sHash = oHashChanger.getHash(); // it will return IT or BPO
Use window object:
var sHash = window.location.hash; // it will return #/IT or #/BPO.
Looking for better answers actually.