Populate control using model of another control - smartphone issue - sapui5

I tried to get the model of the control situated on another view and apply this model to the control on current view.
To do so, I've put the following code into onBeforeRendering() function of the controller:
var oModel = sap.ui.getCore().byId('<id of the control in another view>').getModel('<modelName here>');
sap.ui.getCore().byId('<id of the control in the current view>').setModel(oModel, "<modelName here>");
The problem is that this construction works well on PC and tablet (android) (the control - sap.m.select - is populated with items), but this does not work on smartphone (android) (sap.m.select control is empty).
How do I solve this?

As #mjd suggested in the comment, I've used global model:
sap.ui.getCore().setModel(<shared model data>, "<modelName>");
and then just used the model by its name.
For example:
in page1.controller:
sap.ui.getCore().setModel(data, "selection");
and then in page2.view (the same construction is used in page1.view):
var oSelection = new sap.m.Select({
id: 'selectionID',
items: {
path: "selection>/rootElementName",
template: new sap.ui.core.Item({
text: "{selection>elementName}"
})
},
});

Related

How to get selectedKey of sap.m.Select from outside?

Having this select control:
new Select("id", {
items: {
path: "/cards",
template: new ListItem({
key: "{Kunnr}",
text: "{Descrip}"
}),
},
});
I need it be able to get the selected key of it, but not in the change event. I need it from outside (another function).
I have tried using the ID but I just get undefined as result.
There is a some information missing from your question, mostly around how oSelectMarca is added to your view. This makes a difference in how it's made available in your app. There are two places to get something by Id:
this.getView().byId('id');
sap.ui.getCore().byId('id');
You'll have to check which one it is... Another option is to add that model to your view instead of oSelectMarca in which case your view and your select can share data. But once again that depends on how you add the select to the screen.
Depending on where You need to use it You can do:
this.oSelectMarca = new sap.m.Select('id',{});
this.oSelectMarca.setModel(myModel);
or If You'll be using it in say another controller try:
sap.ui.getCore().oSelectMarca = new sap.m.Select('id',{});
sap.ui.getCore().oSelectMarca.setModel(myModel);
Try with two-way data binding in selectedKey which helps to keep the MV* pattern.
new Select({
selectedKey: "{/selectedCard}" // <-- It's TwoWay
items: {
path: "/cards",
template: new ListItem({
key: "{Kunnr}",
text: "{Descrip}"
}),
},
});
I'm assuming that the default model is accessible across the application. Hence, as long as you can access that model, you can get the selected key by myModel.getProperty("/selectedCard");
I solved it by accesing to the properties from outside using the sap core:
var myvar = sap.ui.getCore().getModel("marcas");
var selectedKey= myvar.getProperty('/cards/Kunnr');

How to set ID of a XML-View in a component environment?

I want to access a view's controller from a custom module with some utility functions. Basically you can do this that way:
var oController = sap.ui.getCore().byId("__xmlview1").getController();
The problem is that the above coding will not work in a real environment because __xmlview1is dynamically created by the framework. So I tried to find a possibility to set the ID of the view during instantiation. The problem is - I could not find one:
Trying to give the view an ID in the view.xml file does not work:
<mvc:View
controllerName="dividendgrowthtools.view.dividendcompare"
id="testID"
xmlns="sap.m"
...
Trying to set the ID in the router configuration of the component does not work either:
...
name: "Dividend Compare",
viewId: "test",
pattern: "Dividend-Compare",
target: "dividendcompare"
...
The problem is that I do not have direct control over the instantiation of the XML view - the component respectively the router does it.
So, is there a solution for that problem? Or at least a save way to get the view ID by providing the name of the view?
You should have a look at the SAPUI5 EventBus.
I am pretty sure, you want to let the controller to do something with the dividentcompare view. With the SAPUI5 Eventbus, you can publish actions from one controller to another witout braking MVC patterns.
In your dividendcompare.controller.js:
onInit : function() {
var oEventBus = sap.ui.getCore().getEventBus();
oEventBus.subscribe("MyChannel", "doStuff", this.handleDoStuff, this);
[...]
},
handleDoStuff : function (oEvent) {
var oView = this.getView();
[...]
}
Now, in your anothercontroller.controller.js:
onTriggerDividendStuff : function (oEvent){
var oEventBus = sap.ui.getCore().getEventBus();
oEventBus.publish("MyChannel", "doStuff", { [optional Params] });
}
You are now able to get the view from the dividentcontroller in every case from every other controller of your app. You dont access the view directly, this would brake MVC patterns, but can pass options to its controller and do the handling there.

How to enable auto focus on SAPUI5 input suggestion field

I'm currently developing a sapui5 mobile application and am using an sap.m.Input with suggestions bound by a model like this:
new Page('page', {
showNavButton: true,
content: [
new sap.m.Input({
id: "input",
type: 'Text',
showSuggestion: true,
suggestionItemSelected: function(event) {
event.getParameters().selectedItem.mProperties.text;
},
liveChange: function() {
// some stuff
}
})
]
});
The Model is created and bound like the following:
var model = new sap.ui.model.json.JSONModel();
// model is filled
sap.ui.getCore().byId('input').setModel(model);
sap.ui.getCore().byId('input').bindAggregation('suggestionItems', '/', new sap.ui.core.Item({
text: "{someField}"
}));
When I now click into the input field on a mobile device, kind of a new screen opens with a new input field, which the user has to manually focus again, what seems like a usability flaw to me.
Is there a nice possibility to enable auto focusing the input field on this new screen, so that the user doesn't has to do it again? Or can this screen be disabled at all on mobile devices?
sap.m.input doesn't seem to have a own method for focusing, or at least I'm not finding one - I already tried using jquery's .focus() on the new input field, but without success.
edit: for clarification, the suggestion works troublefree - only the absence of the auto focus on the appearing new screen is what bothers me.
Here is a workaround to "fix" this behavior: https://jsbin.com/lukozaq
Note 1: The above snippet relies on internal implementation of how the popup works. Use it with caution as there are currently no public APIs to access the corresponding internal controls.
Note 2: I put the word fix in quotes because it seems to be the intended behavior that the user has to click on the second input field explicitly, according to the comment in the source code:
Setting focus to DOM Element, which can open the on screen keyboard on mobile device, doesn't work consistently across devices. Therefore, setting focus to those elements are disabled on mobile devices and the keyboard should be opened by the user explicitly.
That comment is from the module sap.m.Dialog. On a mobile device, when the user clicks on the source input field, a stretched Dialog opens up as a "popup" which has the second input field in its sub header.
Please check the API documentation of sap.m.Input, it has a method focus. You can call:
this.byId("input").focus()
to set the focus into the input field.
Try this:
jQuery.sap.delayedCall(0, this, function() {
this.byId("input").focus()
});
About how to detect when the user presses the input field: maybe something like this? Only problem is that I think you probably don't know the id of the new input field which is shown.
var domId = this.byId("input").getId();
$( "#"+domId ).click(function() {
jQuery.sap.delayedCall(0, this, function() {
this.byId("input").focus()
});
});
I am pretty sure that the first piece of code is how to put focus on an input. I'm not sure about the second part, but it's something to try.
in onAfterRendering() do the below..
onAfterRendering : function() {
$('document').ready(function(){
sap.ui.getCore().byId('input').focus();
});
}
I didnĀ“t have the exactly same problem, but It was similiar.
My problem was that I needed focus into suggestion input when the view had been rendered. My solution was to put following code into "hanldeRouteMatched" function:
var myInput = this.byId("inputName");
var viewName = this.getView().getId();
var selector1 = viewName + "--inputName-inner";
var selector2 = viewName + "--inputName-popup-input-inner";
jQuery.sap.delayedCall(1000, this, function() {
myInput.focus();
if($("#" + selector1)){
$("#" + selector1).click();
jQuery.sap.delayedCall(500, this, function() {
if($("#" + selector2)){
$("#" + selector2).focus();
}
});
}
});
If you see that suggestion input catch focus, but It lose it after, or It never catched it, try increasing the time in delayedCalls. The needed time depends on your connection speed.

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

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());
}

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.