SAP UI5: Navigation from Master page and Detail page through two different OData URLs - sapui5

I am trying to create a Master - Detail page using OData servicea in SAPUI5. Everything works fine in the Master page. Meaning I'm able to populate the list with valid data from SAP backend using an OData URL.
Now what I want to achieve is, to call a second OData URL to fetch the detail values and populate that in the page.
My Master.controller.js
handleListSelect : function (evt) {
var context = evt.getParameter("listItem").getBindingContext();
this.nav.to("Detail", context);
console.log('evt.getSource: ' + evt.getSource());
console.log('evt.getBindingContext: ' + evt.getSource().getBindingContext());
}
Console Output gives
"evt.getSource: Element sap.m.List#Master--list" sap-ui-core.js line 80 > eval:31
"evt.getBindingContext: undefined"
I'm unable to populate values in the detail page from the second URL. Can anyone guide or help me on this?
My Compenent.js
createContent : function() {
// create root view
var oView = sap.ui.view({
id : "app",
viewName : "sap.ui.demo.myFiori.view.App",
type : "JS",
viewData : {
component : this
}
});
// Using OData model to connect against a real service
var url = "/MyFioriUI5/proxy/sap/opu/odata/sap/XXXXXX;mo/";
var oModel = new sap.ui.model.odata.ODataModel(url, true, "", "");
oView.setModel(oModel);
// set i18n model
var i18nModel = new sap.ui.model.resource.ResourceModel({
bundleUrl : "i18n/messageBundle.properties"
});
oView.setModel(i18nModel, "i18n");
// set device model
var deviceModel = new sap.ui.model.json.JSONModel({
isPhone : jQuery.device.is.phone,
isNoPhone : !jQuery.device.is.phone,
listMode : (jQuery.device.is.phone) ? "None" : "SingleSelectMaster",
listItemType : (jQuery.device.is.phone) ? "Active" : "Inactive"
});
deviceModel.setDefaultBindingMode("OneWay");
oView.setModel(deviceModel, "device");
// Using a local model for offline development
// var oModel = new sap.ui.model.json.JSONModel("model/mock.json");
// oView.setModel(oModel);
// done
return oView;
}
My Detail.controller.js
sap.ui.controller("sap.ui.demo.myFiori.view.Detail", {
handleNavButtonPress : function(evt) {
this.nav.back("Master");
},
onBeforeRendering : function() {
// this.byId("SupplierForm").bindElement("BusinessPartner");
},
handleApprove : function(evt) {
// show confirmation dialog
var bundle = this.getView().getModel("i18n").getResourceBundle();
sap.m.MessageBox.confirm(bundle.getText("ApproveDialogMsg"), function(oAction) {
if (sap.m.MessageBox.Action.OK === oAction) {
// notify user
var successMsg = bundle.getText("ApproveDialogSuccessMsg");
sap.m.MessageToast.show(successMsg);
// TODO call proper service method and update model (not part of this session)
}
},
bundle.getText("ApproveDialogTitle"));
}
});

I can't see the second URL you are refering to, but we handle it this way.
In Component.js:
var oView = sap.ui.view({
id: "app",
viewName: "fom.test.app.view.App",
type: "JS",
viewData: { component : this }
});
var dataModel = new sap.ui.model.odata.ODataModel("/fom/fiori/odata/FOM/mobile_app_srv", true);
oView.setModel(dataModel);
This connects the master view to the data for all the list items.
In the App.controller.js we make use of the onNavigation function, defined in Detail.controller.js. That means when routing to the detail view, the onNavigation function is called before the view is set up.
App.controller.js:
to : function (pageId, context) {
var app = this.getView().app;
// load page on demand
var master = ("Master" === pageId);
if (app.getPage(pageId, master) === null) {
var page = sap.ui.view({
id : pageId,
viewName : "fom.test.app.view.view." + pageId,
type : "XML"
});
page.getController().nav = this;
app.addPage(page, master);
jQuery.sap.log.info("app controller > loaded page: " + pageId);
}
// show the page
app.to(pageId);
// set data context on the page
if (context) {
var page = app.getPage(pageId);
page.setBindingContext(context);
try{
var oController = page.getController();
oController.onNavigation(context);
}
catch(e){ }
}
},
Detail.controller.js:
onNavigation: function(context) {
this.getView().bindElement({
path: context.sPath,
parameters: {
select: "Id," +
"Lifnam," +
"Rmwwr," +
"Waers," +
"Sendedatum," +
"Workflowtyp," +
"Sktodat," +
"Stufe," +
"MonFrgstA," +
"Bukrs," +
"Belnr," +
"Gjahr," +
"EdcObject," +
"BstatTxt",
expand: "Positions"
}
});
},
The bindElements() function, connects the detail view to the results of another web service call, which fetches all attributes mentioned in select and the line items which are fetched with the expand.
Now your first webservice call is loading only the data relevant for the master view list, and the second one all the information of the selected list item.
When you use the newer routing functionality of UI5, you will need to find another place for the hook. I haven't built that in yet.

oView.setModel(oModel);
You have this statement twice - the one would override the other - you need to provide an alternate reference to the second instance:
e.g.
oView.setModel(oModel, "local")

Related

How to access component model from outside

I have created a shell-in-shell construct in the index.html:
sap.ui.getCore().attachInit(function () {
// create a new Shell that contains the root view
var oShell = new sap.m.Shell({
id: "appShell",
app: new sap.ui.core.ComponentContainer({
name: "internal_app",
height: "100%"
})
});
// load the view that contains the unified shell
var oAppShellView = sap.ui.view({
type: sap.ui.core.mvc.ViewType.XML,
viewName: "internal_app.view.AppShell"
});
// access the unified shell from the view
var oUnifiedShell = oAppShellView.byId("unifiedShell");
// place the app shell in the unified shell
oUnifiedShell.addContent(oShell);
oAppShellView.placeAt("content");
});
In addition, a default model has been defined in manifest.json:
....
},
"models": {
"": {
"type": "sap.ui.model.json.JSONModel"
}
},
....
In the controller of the view internal_app.view.AppShell (which has been created by the code snippet above) I would now like to access the default model but neither this.getModel() nor this.getOwnerComponent().getModel() (getModel() and getOwnerComponent() return undefined) worked. I assume that the AppShell controller does not have an owner. But how can I access the default model in the onInit of that controller?
The app structure in your case is somewhat unusual - Nevertheless, you can always access the model, defined in manifest.json, as long as you can access the inner component.
Assuming this is referencing the controller of the internal_app.view.AppShell, you can get the default model like this:
onInit: function() {
var innerShell = sap.ui.getCore().byId("appShell"); // only if the app is standalone
this.componentLoaded(innerShell.getApp()).then(this.onComponentCreated.bind(this));
},
componentLoaded: function(componentContainer) {
var component = componentContainer.getComponent();
return component ? Promise.resolve(component) : new Promise(function(resolve) {
componentContainer.attachEventOnce("componentCreated", function(event) {
resolve(event.getParameter("component"));
}, this);
}.bind(this));
},
onComponentCreated: function(component) {
var myDefaultModel = component.getModel(); // model from manifest.json
// ...
}

How to display NotFound when invalid hash is matched in SAPUI5

I did the steps to catch and handle invalid hashes with SAPUI5 but my application is not working.
When i try to navigate to NotFound view changing the Hash i only gets an Info message:
But the view isn't displayed.
[EDIT]:
Adding source code files:
Here i added the bypassed section
I've created the target in Targets section of the manifest:
This is the NotFound.controller.js
sap.ui.define([
"my/path/controller/BaseController"
], function (BaseController) {
"use strict";
return BaseController.extend("my.path.controller.NotFound", {
onInit: function () {
var oRouter, oTarget;
oRouter = this.getRouter();
oTarget = oRouter.getTarget("NotFound");
oTarget.attachDisplay(function (oEvent) {
this._oData = oEvent.getParameter("data"); // store the data
}, this);
},
// override the parent's onNavBack (inherited from BaseController)
onNavBack : function (oEvent){
// in some cases we could display a certain target when the back button is pressed
if (this._oData && this._oData.fromTarget) {
this.getRouter().getTargets().display(this._oData.fromTarget);
delete this._oData.fromTarget;
return;
}
// call the parent's onNavBack
BaseController.prototype.onNavBack.apply(this, arguments);
}
});
});
Here the NotFound.view.xml:
<mvc:View
controllerName="my.path.controller.NotFound"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<MessagePage
title="{i18n>NotFound}"
text="{i18n>NotFound.text}"
description="{i18n>NotFound.description}"
showNavButton="true"
navButtonPress="onNavBack"/>
</mvc:View>
And here the onInit method at the App controller:
onInit: function(){
jQuery.sap.log.setLevel(jQuery.sap.log.Level.INFO);
var oRouter = this.getRouter();
oRouter.attachBypassed(function (oEvent) {
var sHash = oEvent.getParameter("hash");
// do something here, i.e. send logging data to the backend for analysis
// telling what resource the user tried to access...
jQuery.sap.log.info("Sorry, but the hash '" + sHash + "' is invalid.", "The resource was not found.");
});
oRouter.attachRouteMatched(function (oEvent){
var sRouteName = oEvent.getParameter("name");
// do something, i.e. send usage statistics to backend
// in order to improve our app and the user experience (Build-Measure-Learn cycle)
jQuery.sap.log.info("User accessed route " + sRouteName + ", timestamp = " + new Date().getTime());
});
}
and
Any can help me?
Regards,
Check this plunker:
https://plnkr.co/edit/pxOkRSM8c97hXO6gkbpV
The key config is this on manifest.json:
"targets": {
"tgHome": {
"viewPath": "sapui5Example",
"viewName": "home"
},
"notFound": {
"viewPath": "sapui5Example",
"viewName": "NotFound",
"transition": "show"
}
}
To fire the 'not found' route, download the plunker and in the URL, after the hash just type anything and you will the the not Found Page (if you do it directly on plunker it won't work). Here is a pic:

add input row at the beginning ui5

I'm having problems concerning adding a row with input field to add item to the table. I'm trying to use this example
addEntry : function(oEvent) {
var path = oEvent.getSource().getBindingContext().getPath();
var obj = {
fname: null,
lname: null,
desc: null,
createNew: false,
removeNew: false,
saveNew: true
};
var oModel = this.getView().getModel();
oModel.setProperty(path, obj);
},
The only difference that i want is for the row to be visible from the start (without the + icon) so that the user can directly add fields and when OK is clicked, a new row needs to be added.
Thank you
You should remove the addEntry() function as it is just triggering from the Add Icon and instead create the object on your onInit() function.
It will look like this:
onInit : function() {
var dummyData = [{"fname": "",
"lname": "",
"desc": "",
"removeNew": false,
"saveNew": true}];
var oModel = new sap.ui.model.json.JSONModel({data : dummyData});
this.getView().setModel(oModel);
}
When you click on the save button, the saveEntry() function will be triggered and the new entry will be pushed to the model.
A new form will show up by calling the addEmptyObject() function at the end of it.
Here is a working example:
https://jsbin.com/wutefaz/edit?html,js,output

How to get the data of a view

I´m using SAPUI5, I have a MasterPage and a DetailPage, in the MasterPage I have a List and when I select de Item in the List the information is displayed in the DetailPage.
In the DetailPage I have a PositiveAction, When I press the PositiveAction I need to get the Data of the DetailPage but I don't know how to do this.
My code of the Item Press
onPoSelect : function(oEvent) {
var oListItem = oEvent.getParameter('listItem');
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("DetailPanel", {
invoicePath: oListItem.getBindingContext("solped").getPath().substr(1)
});
},
My code in the DetailPanel
onInit: function (){
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("DetailPanel").attachPatternMatched(this._onObjectMatched, this);
},
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/" + oEvent.getParameter("arguments").invoicePath,
model: "solped"
});
},
The line "oEvent.getParameter("arguments").invoicePath,"
returns this.
Invoices(CustomerName='Alfreds Futterkiste',Discount=0f,OrderID=10702,ProductID=3,ProductName='Aniseed Syrup',Quantity=6,Salesperson='Margaret Peacock',ShipperName='Speedy Express',UnitPrice=10.0000M)
I have the information but it is a String, How can I convert this String in an Object? Or, how else can I access the information in the view?
The image of the View
enter image description here
I assume you can already see the data of the detail in your Detail view.
You binded the data to the view by bindElement function and to retrieve them back in the code you are looking for "getBindingContext" function.
Create following function in your Detail controller:
// this must be connected to Button -> <Button press="onPositivePress">
onPositivePress: function(oEvent) {
var oBindingContext = this.getView().getBindingContext("solped");
// this is the path you can use to call odata service
var sPath = oBindingContext.getPath();
// this is data you are looking for
var oReqData = oBindingContext.getObject();
}
You can get all the properties as an object by passing the binding path as an argument to the getProperty function of the underlying Data model.
var oModel = this.getView().getModel("solped");
var oProps = oModel.getProperty(oListItem.getBindingContext("solped").getPath());
You can then access these properties as
oProps.CustomerName;
oProps.OrderID;
...
for converting string to object see below example.
var a = "how r u";
var b = [a];
you will get object of a in b.

SAPUI5- MyRouter.js not working properly

I am following the MVC model of SAPUI5. I have a MyRouter.js which contains the following code:
jQuery.sap.declare("sap.ui.demo.Onepage.MyRouter");
sap.ui.demo.Onepage.MyRouter={
/**
* to monkey patch the router with the mobile nav back handling
*/
myNavBack : function (route, data) {
var history = sap.ui.core.routing.History.getInstance();
var url = this.getURL(route, data);
var direction = history.getDirection(url);
if ("Backwards" === direction) {
window.history.go(-1);
} else {
var replace = true; // otherwise we go backwards with a forward history
this.navTo(route, data, replace);
}
},
/**
* to monkey patch the router with a nav to method that
* does not write hashes but load the views properly
*/
myNavToWithoutHash : function (viewName, viewType, master, data) {
var oapp = sap.ui.getCore().byId("Myapp");
var oview = this.getView(viewName, viewType);
oapp.addPage(oview, master);
oapp.to(view.getId(), "show", data);
}
};
Component.js:
jQuery.sap.declare("sap.ui.demo.Onepage.Component");
sap.ui.core.UIComponent.extend("sap.ui.demo.Onepage.Component", {
metadata : {
routing : {
config : {
viewType : "XML",
viewPath : "view",
targetControl : "Myapp",
targetAggregation: "pages",
clearTarget : false,
transition: "slide"
},
routes : [
{
pattern : "",
name : "Myapp",
view : "App",
viewPath : "view",
targetAggregation: "pages",
viewLevel : 1,
}]
}
},
/**
* !!! The steps in here are sequence dependent !!!
*/
init : function () {
// 1. some very generic requires
jQuery.sap.require("sap.m.routing.RouteMatchedHandler");
jQuery.sap.require("sap.ui.demo.Onepage.MyRouter");
// 2. call overridden init (calls createContent)
sap.ui.core.UIComponent.prototype.init.apply(this, arguments);
// 3a. monkey patch the router
var router = this.getRouter();
router.myNavBack = sap.ui.demo.Onepage.MyRouter.myNavBack;
router.myNavToWithoutHash = sap.ui.demo.Onepage.MyRouter.myNavToWithoutHash;
if (!sap.ui.Device.system.phone) {
router.myNavToWithoutHash("view.hello", "XML", false);
}
// 4. initialize the router
this.routeHandler = new sap.m.routing.RouteMatchedHandler(router);
router.initialize();
},
destroy : function () {
if (this.routeHandler) {
this.routeHandler.destroy();
}
// call overridden destroy
sap.ui.core.UIComponent.prototype.destroy.apply(this, arguments);
},
createContent : function () {
// create root view
var oView = sap.ui.view({
id : "app",
viewName : "view.App",
type : "XML",
viewData : { component : this }
});
// set navigation model
// load the global data model
/*var oJSONDataModel = new sap.ui.model.json.JSONModel("model/data.json");
oView.setModel(oJSONDataModel);
// load the global image source model
var oImgModel = new sap.ui.model.json.JSONModel("model/img.json");
oView.setModel(oImgModel, "img"); */
// done
return oView;
}
});
Everything is working fine until i call the myNavToWithoutHash function from any controller. The error i get is:
Uncaught TypeError: Cannot read property 'addPage' of undefined - MyRouter.js :27
I cannot find the possible issue. Please help.
XMLViews do automatic prefixing of IDs. So the real ID of your sap.m.App in the DOM probably is sth. like "__xmlview0--MyApp" whereas "__xmlview0" would be your views id if it has any (just check in Elements Tab). Querying for a control with sap.ui.getCore().byId() will not know/respect any of this prefixes. It searches for the id "MyApp" which is obviously != "__xmlview0--MyApp". Since luckily your XMLView has the id "app" you should be fine by doing this:
sap.ui.getCore().byId("app--Myapp");
So remember: Whenever you use XMLViews you can't use sap.ui.getCore().byId('MyApp') but have to use this.getView().byId('MyApp') in your views controller.
Furthermore please note that you can use custom router classes by just declaring so in your routing config:
metadata : {
routing : {
config : {
routerClass: sap.ui.demo.Onepage.MyRouter,
viewType : "XML",
viewPath : "view",
targetControl : "Myapp",
targetAggregation: "pages",
clearTarget : false,
transition: "slide"
}
...
As you might have already guessed your Router therefore needs to extend sap.ui.core.routing.Router!
HF
Christian
Your error seems to related to not finding the the App.
var oapp = sap.ui.getCore().byId("Myapp");
You need to declare controllerName attribute for your controller class in your App.view.xml.
<mvc:View height="100%" xmlns:mvc="sap.ui.core.mvc" controllerName="view.App"
xmlns:l="sap.ui.layout" xmlns:core="sap.ui.core" xmlns="sap.m">
<App id="Myapp" />
</mvc:View>