Navigation Problems in a sapui5 unified Shell (sap.ui.unified.Shell) - sapui5

I have used unified Shell control in order to implement the facebook-like swipe menu and I integrated in it a list so that I can enter menu items. The idea is that when a user clicks on a certain list item in the menu he will get redirected to a new view. I tried to implement it by using bus.publish("nav", "to" {id:..}) but it's not working. (I have put the menu in the Curtain Pane of the Unified Shell) Can anybody help me?
You can find below the respective code snippets of the view and controller.
var oListTemplate = new sap.m.StandardListItem({
title: "{title}",
icon: "{icon}",
description: "{description}",
type: sap.m.ListType.Navigation,
customData: new sap.ui.core.CustomData({
key: "targetPage",
value: "{targetPage}"
})
});
var oList = new sap.m.List({
selectionChange: [oController.doNavOnSelect, oController],
mode: sap.m.ListMode.SingleSelectMaster
});
oList.bindAggregation("items", "/Menu", oListTemplate);
The controller:
onInit: function() {
this.getView().setModel(new sap.ui.model.json.JSONModel("model/menu.json"));
this.bus = sap.ui.getCore().getEventBus();
},
doNavOnSelect: function(event){
if (sap.ui.Device.system.phone) {
event.getParameter("listItem").setSelected(false);
}
this.bus.publish("nav", "to", {
id: event.getParameter('listItem').getCustomData()[0].getValue()
});

Navigation via the sap.ui.core.EventBus is obsolete.
Please have a look at "Navigation and Routing" https://help.sap.com/docs/SAP_NETWEAVER_AS_ABAP_FOR_SOH_740/468a97775123488ab3345a0c48cadd8f/688f36bd758e4ce2b4e682eef4dc794e.html?locale=en-US
A new Routing mechanism was introduced to SAPUI5 in release 1.16. For in-app navigation, this supersedes previous techniques such as using the sap.ui.core.EventBus or sharing navigation-container specific controller code amongst aggregated pages.

Solution: Replace bus.publish with app.to
doNavOnSelect: function(event){
if (sap.ui.Device.system.phone) {
event.getParameter("listItem").setSelected(false);
}
app.to(event.getParameter('listItem').getCustomData()[0].getValue());
}

Related

How to get clicked sap.m.Table - Item data from sap.m.Menu - used as context menu?

I have a sap.m.Table in my View with some Items.
Now I have implemetned sap.m.Menu as a context menu to this table.
Am I able to get the Table Item a clicked in menuItemPressed event of the sap.m.MenuItem?
Example here:
https://jsfiddle.net/dusansacha/Lbgja7r8/37/
onInit: function() {
function menuItemPressed(oEvent) {
console.log(oEvent.getSource().getId());
console.log(oEvent.getSource().getParent().getId());
// How to get selected Table-ColumnListItem information?
console.log(oEvent.getSource().getParent().getParent().getId());
}
this.getView().byId("idTable").setContextMenu(
new sap.m.Menu({
items: [
new sap.m.MenuItem({text: "Menu Item1", press: menuItemPressed}),
new sap.m.MenuItem({text: 'Menu Item2', press: menuItemPressed})
]
}));
}
One solution which came to my mind is to use Custom Data on sap.m.MenuItem. But this seems not right for me.
Any suggestions?
In your onInit function you can attach the beforeOpenContextMenu event as follows:
this.getView().byId("idTable").attachBeforeOpenContextMenu((event) => {
//do something useful with event.getParameter("listItem")
});
This event is fired when the context menu is opened. When the context menu is opened, the binding context of the item is set to the given contextMenu (from the documentation).
For further reference, here the link to the API reference

Uncaught TypeError: Cannot read property 'setVisible' of undefined

Im fairly new to SAPUI5 and when I click on button I get the error in the title
what I did in Is I used the SAP web IDE to create new MVC project .
in the main view JS I put
createContent : function(oController) {
var btn = new sap.m.Button({
id:"myBtn",
text : "Content Button"
});
return new sap.m.Page({
title: "TitleT",
content: [ btn ]
});
}
in the Main controller JS I put the following code
onInit: function() {
var that = this;
window.setTimeout(function() {
that.byId("myBtn").setVisible(true);
}, Math.random() * 10000);
},
onPress: function() {
this.byId("pressMeButton").setText("I got pressed");
}
When I run it I see the button but when I click on it I get the error in the on Init,
what am I doing wrong here?
The actual problem with your code is that you create a static id in your javascript view, but the controller will search the id with a prefix like "__jsview0--myBtn" if you call that.byId("myBtn").
Therefore you either have to use createId("myBtn") in your javascript view for defining the id or sap.ui.getCore().byId("myBtn") in the controller and it will work fine. The first approach is recommended though to avoid name clashes.
PS:
i did not really get the use case, it seems like you want to display the button only after a certain (random) timeframe. But the visible flag by default is already true, so the button will always be visible.
Use the standard timeout and byId function from SAPUI5 like this:
onInit: function() {
setTimeout(function() {
sap.ui.getCore().byId("myBtn").setVisible(true);
}, Math.random() * 10000);
},

How include the root view in the shell

I see this example
https://openui5.hana.ondemand.com/explored.html#/entity/sap.ui.unified.Shell/samples
Now I have a root view (this.app) and all works fine.
return this.app;
Now I want include the root app in a shell container..
I try to do this:
var oShell= new sap.m.Shell("idShell", {
title: "Test App",
logo:"https://dl.dropboxusercontent.com/u/7546923/OpenUI5/SAPUI5.png",
headerRightText: "This is a sap.m.Shell",
logout: function() {alert("Logout button was pressed");}
});
oShell.setApp(this.app);
return oShell;
But in this way I can't see the bar on the top. (I see only the app limited in width and the logo out of the app, on left)
I try to do this:
var oShell= new sap.m.Shell("idShell", {
title: "Test App",
logo:"https://dl.dropboxusercontent.com/u/7546923/OpenUI5/SAPUI5.png",
headerRightText: "This is a sap.m.Shell",
logout: function() {alert("Logout button was pressed");}
});
oShell.setApp(sap.ui.xmlview("view.shell")); //the view of the example in the documentation
return oShell;
In this way I can see the bar but I can't associate the root app in the content of shell
Now I use sap.ui.unified.Shell
jQuery.sap.require("sap.ui.unified.Shell");
jQuery.sap.require("sap.ui.unified.ShellHeadItem");
this.shell = new sap.ui.unified.Shell('');
var logout=new sap.ui.unified.ShellHeadItem({
tooltip: "Logout",
icon: "sap-icon://menu2"
});
this.shell.addHeadItem(logout);
this.shell.addContent(this.app);
How can I use a MVC pattern to manage the shell bar content? I want use an XML-view. And how can I avoid to write
jQuery.sap.require("sap.ui.unified.Shell");
jQuery.sap.require("sap.ui.unified.ShellHeadItem");
?
This works for me:
new sap.m.Shell("Shell", {
app: sap.ui.jsview("RootView", "my-app.view.App")
}).placeAt("root");
While the root control of my-app.view.App is a sap.m.SplitApp (or sap.m.App). Note that the sap.m.Shell is just providing a frame it is NOT providing any NavContainer, Page or similar concept you usually use in sap.m. What is the root control in your view.shell? From the docs:
getApp() : sap.ui.core.Control
Getter for aggregation app. A Shell contains an App or a SplitApp
(they may be wrapped in a View). Other control types are not allowed.
Btw: Your example link is not working for me.
BR
Chris

Get passed data on next page after calling "to" from NavContainer

I am on my way building a Fiori like app using SAPUI5. I have successfully built the Master page, and on item click, I pass the context and navigate to Detail page.
The context path from Master page is something like /SUPPLIER("NAME"). The function in App.controoler.js is as follows:
handleListItemPress: function(evt) {
var context = evt.getSource().getBindingContext();
this.myNavContainer.to("Detail", context);
// ...
},
But I would like to know how I can access this context in the Detail page. I need this because I need to use $expand to build the URL and bind the items to a table.
There is an example in the UI5 Documentation on how to deal with this problem using an EventDelegate for the onBeforeShow function which is called by the framework automatically. I adapted it to your use case:
this.myNavContainer.to("Detail", context); // trigger navigation and hand over a data object
// and where the detail page is implemented:
myDetailPage.addEventDelegate({
onBeforeShow: function(evt) {
var context = evt.data.context;
}
});
The evt.data object contains all data you put in to(<pageId>, <data>). You could log it to the console to see the structure of the evt object.
Please refer the "shopping cart" example in SAP UI5 Demo Kit.
https://sapui5.hana.ondemand.com/sdk/test-resources/sap/m/demokit/cart/index.html?responderOn=true
Generally, in 'Component.js', the routes shall be configured for the different views.
And in the views, the route has to be listened to. Please see below.
In Component.js:
routes: [
{ pattern: "cart",
name: "cart",
view: "Cart",
targetAggregation: "masterPages"
}
]
And in Cart.controller.js, the route has to be listened. In this example, cart is a detail
onInit : function () {
this._router = sap.ui.core.UIComponent.getRouterFor(this);
this._router.attachRoutePatternMatched(this._routePatternMatched, this);
},
_routePatternMatched : function(oEvent) {
if (oEvent.getParameter("name") === "cart") {
//set selection of list back
var oEntryList = this.getView().byId("entryList");
oEntryList.removeSelections();
}
}
Hope this helps.

Switch class on tabs with React.js

So I have a tab-component that has 3 items:
React.DOM.ul( className: 'nav navbar-nav',
MenuItem( uid: 'home')
MenuItem( uid: 'about')
MenuItem( uid: 'contact)
)
And in the .render of MenuItem:
React.DOM.li( id : #props.uid, className: #activeClass, onClick: #handleClick,
React.DOM.a( href: "#"+#props.uid, #props.uid)
)
Every time I click an item, a backbone router gets called, which will then call the tab-component, which in turn will call a page-component.
I'm still trying to wrap my head around the fact there's basically a one-way data-flow. And I'm so used to manipulating the DOM directly.
What I want to do, is add the .active class to the tab clicked, and make sure it gets removed from the inactive ones.
I know the CSS trick where you can use a data- attribute and apply different styling to the attribute that is true or false.
The backbone router already has already gotten the variable uid and calls the right page. I'm just not sure how to best toggle the classes between tabs, because only one can be active at the same time.
Now I could keep some record of which tab is and was selected, and toggle them etc. But React.js already has this record-keeping functionality.
The #handleClick you see, I don't even want to use, because the router should tell the tab-component which one to give the className: '.active' And I want to avoid jQuery, because React.js doesn't need direct DOM manipulation.
I've tried some things with #state but I know for sure there is a really elegant way to achieve this fairly simple, I think I watched some presentation or video of someone doing it.
I'm really have to get used to and change my mindset towards thinking React-ively.
Just looking for a best practice way, I could solve it in a really ugly and bulky way, but I like React.js because it's so simple.
Push the state as high up the component hierarchy as possible and work on the immutable props at all levels below. It seems to make sense to store the active tab in your tab-component and to generate the menu items off data (this.props in this case) to reduce code duplication:
Working JSFiddle of the below example + a Backbone Router: http://jsfiddle.net/ssorallen/4G46g/
var TabComponent = React.createClass({
getDefaultProps: function() {
return {
menuItems: [
{uid: 'home'},
{uid: 'about'},
{uid: 'contact'}
]
};
},
getInitialState: function() {
return {
activeMenuItemUid: 'home'
};
},
setActiveMenuItem: function(uid) {
this.setState({activeMenuItemUid: uid});
},
render: function() {
var menuItems = this.props.menuItems.map(function(menuItem) {
return (
MenuItem({
active: (this.state.activeMenuItemUid === menuItem.uid),
key: menuItem.uid,
onSelect: this.setActiveMenuItem,
uid: menuItem.uid
})
);
}.bind(this));
return (
React.DOM.ul({className: 'nav navbar-nav'}, menuItems)
);
}
});
The MenuItem could do very little aside from append a class name and expose a click event:
var MenuItem = React.createClass({
handleClick: function(event) {
event.preventDefault();
this.props.onSelect(this.props.uid);
},
render: function() {
var className = this.props.active ? 'active' : null;
return (
React.DOM.li({className: className},
React.DOM.a({href: "#" + this.props.uid, onClick: this.handleClick})
)
);
}
});
You can try react-router-active-componet - if you working with boostrap navbars.
You could try to push the menu item click handler up to it's parent component. In fact I am trying to do something similar to what you are doing.. I have a top level menubar component that I want to use a menubar model to render the menu bar and items. Other components can contribute to the top level menubar by adding to the menubar model... simply adding the top level menu, the submenuitem, and click handler (which is in the component adding the menu). The top level component would then render the menubar UI and when anything is clicked, it would use the "callback" component click handler to call to. By using a menu model, I can add things like css styles for actice/mouseover/inactive, etc, as well as icons and such. The top level menubar component can then decide how to render the items, including mouse overs, clicks, etc. At least I think it can.. still working on it as I am new to ReactJS myself.