SAPUI5- MyRouter.js not working properly - sapui5

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>

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:

SAPUI5 navigation and return

I have 2 views: in master1 I display a list of items and when I click on one item, I navigate to master2. The code in the controller master1.js looks like this:
onSelect : function(oEvent) {
this.getRouter().navTo("master2", {
from : "master1",
zlonr : oLoadingOrder.LoNumber
});
},
In master2.js in onRouteMatched I have this code:
onRouteMatched : function(oEvent) {
var that = this;
var oParameters = oEvent.getParameters();
if (oParameters.name === "master2") {
var oView = this.getView();
var oDialog = oView.byId("dialogBaseInfo");
if (!oDialog) {
oDialog = sap.ui.xmlfragment(oView.getId(),"ZABC.view.fragment.BaseInfo",this);
oView.addDependent(oDialog);
}
this.setCurrentDateTime();
oDialog.open();
},
So I am simply opening a dialog.
I can see that the dialog opens, but then it closes again right away but it goes to master2 screen. There is no error in the debugger for it.
My routing looks like this:
pattern: "master1",
name: "master1",
view: "Master1",
// viewLevel: 1,
targetAggregation: "pages",
clearControlAggregation: true,
targetControl: "masterAppId",
subroutes: [
{
pattern: "zlonr/{zlonr}",
name: "master2",
view: "Master2",
// viewLevel: 1,
targetAggregation: "pages",
Any ideas why this happens? The dialog should stay of course open and continue on there.
but then it closes again right away
By default, the target handler, which is created by the router, automatically closes all dialog instances when navigation occurs. The target handler offers a method to change this behavior with setCloseDialogs. Before navigating to "master2", try to add the following line:
this.getRouter().getTargetHandler().setCloseDialogs(false);
For other unexpected behaviors, try to avoid using deprecated properties such as subroutes. Instead, use targets.yourTargetName.parent. Take a look at this tutorial step to see how parent can be used. Also clearControlAggregation shouldn't be used if there is no sap.ui.ux3.Shell in use which is deprecated as well.

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

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")

How to add ColumnListItem to a table inside a page in MVC from other page controller

I have a SAPUI5 application written in MVC
I have a view called oPage4:
var landscapePage = new sap.m.Page({
title : "Landscape Name",
showNavButton : true,
navButtonPress : [oController.back,oController],
footer : new sap.m.Bar({
id : 'landscapePage_footer',
contentMiddle : [
new sap.m.Button({
}),
new sap.m.Button({
})
]
}),
});
oLandscapePageTable = new sap.m.Table("landscape", {
inset : true,
visible : true,
getIncludeItemInSelection : true,
showNoData : false,
columns : [ new sap.m.Column({
styleClass : "name",
hAlign : "Left",
header : new sap.m.Label({
})
}) ]
});
landscapePage.addContent(oLandscapePageTable);
return landscapePage;
then inside page1 controller I want to add a columnlistitem to the table of page 4.
var oPage4 = sap.ui.getCore().byId("p4");
var landscapePageRow = new sap.m.ColumnListItem({
type : "Active",
visible : true,
selected : true,
cells : [ new sap.m.Label({
text : something
}) ]
});
oPage4.getContent().addItem(landscapePageRow);
it doesn't work. please show me how to do so?
Ok, I think I understood your problem now. In general I would avoid calling the page and doing manipulations on it from another view. However, it is absolutely possible:
Additional functions in your view
You can extend your page4 with some more functions that can be called from outside like this:
sap.ui.jsview("my.page4", {
createContent : function() {
this.table = ...
...
},
addColumnListItem : function(columnListItem) {
// add it to the table calling this.table ...
}
}
From another view you´re now able to call this function like this:
var page4 = sap.ui.jsview("my.page4");
page4.addColumnListItem(page4, columnListItem);
Attention: The page4 object itself doesn´t point to the control you´re returning but to the the view instance itself. You will notice this, if you log the page4 object to the console. This is why you have to add functions like described.
Some other approaches would be to use the EventBus like described here to publish and subscribe to events. Since you´ve asked for it let me show you how you could do it:
Using the EventBus
The main intention is, that one can subscribe to a particular event and others can publish such events to the eventbus. Let me give you an example:
Subscribing to the EventBus:
var eventBus = sap.ui.getCore().getEventBus();
eventBus.subscribe("channel1", "event1", this.handleEvent1, this);
Of course you can name your channel and events as you wish. The third parameter indicates the function, that will be called in case of published events. The last paramter is the scope 'this' will point to in the given function.
Your handleEvent1 function could look like this:
handleEvent1 : function(channel, event, data) {
var listItem = data.listItem
}
Publishing events to the EventBus:
var columnListItem = ...
var eventBus = sap.ui.getCore().getEventBus();
eventBus.publish("channel1", "event1",
{
listItem : columnListItem
}
);
One more option you have is to make the columnListItems depending on a model. Like everytime it depends on your actual architecture and data.
Let me know if this solved your problem or if you need some more information.