Control event not triggered even though control property is updated from model - sapui5

I have an input box as you can see in the code below.
The data inside the input box is set using model#setProperty, but my onValueChange gets triggered only when I enter the value directly into the input box. It's not triggered when the value is manipulated via binding.
<!-- ... -->
<table:Column xmlns:table="sap.ui.table">
<Label xmlns="sap.m" text="{i18n>ItemCode}" required="true" />
<table:template>
<Input value="{model1>itemCd}" change=".onValueChange" />
</table:template>
</table:Column>
<!-- ... -->
onValueChange: function(oEvent){
console.log("inside function onValueChange");
},

Try with liveChange beacause from library doesn't exist event change, but event liveChange. Perhaps is deprecated?

As #Marc mentioned change event is part of the Input, not of the model. So it is only triggered if you change the actual Input and not the bound value. Its true but there is a workaround for it.
You can achieve it using input fireLiveChange event and formatter.
View.xml
<Input value="{path: 'ipModel>/text', formatter: 'assets.util.Formatter.triggerLiveChange'}"
liveChange="ipLiveChange" />
Controller.js
onInit: function() {
this.setInputModel();
},
setInputModel:function() {
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({ text: "Test Value"});
this.getView().setModel(oModel, "ipModel");
},
ipLiveChange: function(oEvent) {
console.log("LiveChange triggered!!");
}
Formatter.js
jQuery.sap.declare("assets.util.Formatter");
assets.util.Formatter = {
triggerLiveChange: function (value) {
this.fireLiveChange();
return (value);
}
};

Related

How to register contextmenu event if cell of sap.ui.table is of type input

I try to get an context menu on a table cell which is of type input.
The right mouse click only works in the part outside of the input field.
Is there a way to propergate the event from the input field to the cell below?
Also there does not seem to be an click event on an input field.
My tries can be seen in the plnkr
https://plnkr.co/edit/BMozXm7uRPNlgzUf
codewise:
<t:Table rows="{/}" visibleRowCount="100"
minAutoRowCount="10" visibleRowCountMode="Auto" id="table0"
filter="onTableFilter" class="sapUiNoMargin sapUiNoContentPadding"
beforeOpenContextMenu="onContextMenu">
<t:columns>
<t:Column width="4em" filterProperty="CompCode"
sortProperty="CompCode" resizable="true" autoResizable="true"
class="sapUiLargeNegativeMarginBeginEnd"
click="oninputclick"
press="oninputclick">
<Label text="Comp Code" wrapping="true" class="test_maybe_he"/>
<t:template>
<Input value="{CompCode}" class="test_maybe_he" click="oninputclick"
press="oninputclick"/>
</t:template>
</t:Column>
I have the beforeOpenContextMenu="onContextMenu" in the table tag.
And click="oninputclick" press="oninputclick" in the input tag.
right click is only registerd outside of the input field. (In the sapui5 samples with an Text tag it seems to work.)
I suggest a custom control which inherits from sap.m.Input.
This control should have a new event (e.g. rightPress) and this event should be fired when the native browser event onContextMenu is triggered. Also the native context menu should not be shown.
sap.ui.define([
"sap/m/Input"
], function (Input) {
"use strict";
return Input.extend("gsan.ruleedit.control.MyInput", {
metadata: {
events: {
rightPress: {}
}
},
renderer: {},
oncontextmenu: function(oEvent) {
this.fireRightPress();
oEvent.preventDefault();
}
});
});
You can then use your custom control like any other
<mvc:View xmlns:my="gsan.ruleedit.control"
... />
<my:MyInput value="{CompCode}" rightPress="onContextMenu" />
Working sample: https://plnkr.co/edit/XAfC7SGpdf3RxiDF

How to set the input of a combobox to read-only

In one of my UI5-Dialogs, I implemented a combobox which is invisible when the screen is initially loaded.
In the method onAfterRendering, I start with setting the input to read-only:
onAfterRendering: function(oEvent) {
var oShovel = this.getView("View0200").byId("comboShovel");
oShovel.$().find("input").attr("readonly", true);
this.setVisibleByListKey();
},
After this the method setVisibleByListKey is called, the property visibleShovel will be set to false.
setVisibleByListKey: function(oEvent) {
var oModel = this.getView("View0200").getModel("Data0200");
this.setVisibleByListKey1(oModel);
// ...
},
setVisibleByListKey1: function(oModel) {
oModel.setProperty("/visibleShovel", false);
},
The property is bound to the attribute visible on my combobox.
Because of this behavior, the method onAfterRendering will be called again, the attribute readonly is not available (because of invisibility).
<ComboBox id="comboShovel"
editable="true"
enabled="true"
visible="{Data0200>/visibleShovel}"
valueState="None"
change=".changeCombo">
<items>
<core:Item text="Ja" enabled="true" key="0" />
<core:Item text="Nein" enabled="true" key="1" />
<core:Item text="Nicht erforderlich" enabled="true" key="2" />
</items>
</ComboBox>
I tried to call the set method in onInit or onBeforeRendering but at this time the input attributes can not be changed (because of invisibility again).
So how can I set the input of the combobox to read-only when I set the named visible property?
Solution would be either to use sap.m.Select or to implement a "change" event handler for the sap.m.Combobox and use a coding similar to this sample:
handleChange: function(oEvent) {
var oValidatedComboBox = oEvent.getSource();
var sSelectedKey = oValidatedComboBox.getSelectedKey();
var sValue = oValidatedComboBox.getValue();
if (!sSelectedKey && sValue) {
oValidatedComboBox.setValueState("Error");
oValidatedComboBox.setValueStateText("Please enter a valid country!");
} else {
oValidatedComboBox.setValueState("None");
}
},
Instead of using jquery, use UI5 control's methods and properties:
The sap.m.ComboBox borrows the following two methods from sap.m.InputBase:
setEditable
setEnabled
or since you are using property binding for the visibility, do the same for the editable property, e.g. {Data0200>/editableShovel}

ColumnListItem - Event Handler "press" NOT Triggered

I am using ColumnListItem to display a list of Sales Orders in Overview.view.xml. When the user clicks on an item of the list (of Sales Orders), the App should navigate to the Detail.view.xml.
I have defined the onPress event handler in Overview.Controller.js. But the App did not execute the function (I put an alert() there and it was not triggered). Why the onPress() is not triggered? How do I debug?
<Table items="{myOdata>/SalesOrderSet}">
<ColumnListItem type="Navigation" detailPress=".onPress">
<!-- ... -->
</ColumnListItem>
<columns>
<!-- ... -->
</columns>
</Table>
onPress: function (oEvent) {
//This code was generated by the layout editor.
alert("In");
var loOverview = "Data from Overview";
var oItem = oEvent.getSource();
var loRouter = sap.ui.core.UIComponent.getRouterFor(this);
loRouter.navTo("Detail", {
value: oItem.getBindingContext("oModel").getPath().substr(1)
});
},
The press function is not working as you have not written the correct handler for it. As per your code, the handler is written for detailPress. Just a typo, change the handler to press and it should just work.
Current:
<ColumnListItem type="Navigation" detailPress=".onPress">
Change required:
<ColumnListItem type="Navigation" press=".onPress">
the property that you should bind on the Table control is itemPress and your ColumnListItem need to have the type equals to Navigation
Can you check those?

Passing Data from Master to Detail Page

I watched some tutorials about navigation + passing data between views, but it doesn't work in my case.
My goal is to achieve the follwing:
On the MainPage the user can see a table with products (JSON file). (Works fine!)
After pressing the "Details" button, the Details Page ("Form") is shown with all information about the selection.
The navigation works perfectly and the Detail page is showing up, however the data binding doesnt seem to work (no data is displayed)
My idea is to pass the JSON String to the Detail Page. How can I achieve that? Or is there a more elegant way?
Here is the code so far:
MainView Controller
sap.ui.controller("my.zodb_demo.MainView", {
onInit: function() {
var oModel = new sap.ui.model.json.JSONModel("zodb_demo/model/products.json");
var mainTable = this.getView().byId("productsTable");
this.getView().setModel(oModel);
mainTable.setModel(oModel);
mainTable.bindItems("/ProductCollection", new sap.m.ColumnListItem({
cells: [new sap.m.Text({
text: "{Name}"
}), new sap.m.Text({
text: "{SupplierName}"
}), new sap.m.Text({
text: "{Price}"
})]
}));
},
onDetailsPressed: function(oEvent) {
var oTable = this.getView().byId("productsTable");
var contexts = oTable.getSelectedContexts();
var items = contexts.map(function(c) {
return c.getObject();
});
var app = sap.ui.getCore().byId("mainApp");
var page = app.getPage("DetailsForm");
//Just to check if the selected JSON String is correct
alert(JSON.stringify(items));
//Navigation to the Detail Form
app.to(page, "flip");
}
});
Detail Form View:
<mvc:View xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:f="sap.ui.layout.form" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" controllerName="my.zodb_demo.DetailsForm">
<Page title="Details" showNavButton="true" navButtonPress="goBack">
<content>
<f:Form id="FormMain" minWidth="1024" maxContainerCols="2" editable="false" class="isReadonly">
<f:title>
<core:Title text="Information" />
</f:title>
<f:layout>
<f:ResponsiveGridLayout labelSpanL="3" labelSpanM="3" emptySpanL="4" emptySpanM="4" columnsL="1" columnsM="1" />
</f:layout>
<f:formContainers>
<f:FormContainer>
<f:formElements>
<f:FormElement label="Supplier Name">
<f:fields>
<Text text="{SupplierName}" id="nameText" />
</f:fields>
</f:FormElement>
<f:FormElement label="Product">
<f:fields>
<Text text="{Name}" />
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:formContainers>
</f:Form>
</content>
</Page>
</mvc:View>
Detail Form Controller:
sap.ui.controller("my.zodb_demo.DetailsForm", {
goBack: function() {
var app = sap.ui.getCore().byId("mainApp");
app.back();
}
});
The recommended way to pass data between controllers is to use the EventBus
sap.ui.getCore().getEventBus();
You define a channel and event between the controllers. On your DetailController you subscribe to an event like this:
onInit : function() {
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. Function to be executed, 4. Listener
eventBus.subscribe("MainDetailChannel", "onNavigateEvent", this.onDataReceived, this);)
},
onDataReceived : function(channel, event, data) {
// do something with the data (bind to model)
console.log(JSON.stringify(data));
}
And on your MainController you publish the Event:
...
//Navigation to the Detail Form
app.to(page,"flip");
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. the data
eventBus.publish("MainDetailChannel", "onNavigateEvent", { foo : "bar" });
...
See the documentation here: https://openui5.hana.ondemand.com/docs/api/symbols/sap.ui.core.EventBus.html#subscribe
And a more detailed example:
http://scn.sap.com/community/developer-center/front-end/blog/2015/10/25/openui5-sapui5-communication-between-controllers--using-publish-and-subscribe-from-eventbus
Even though this question is old, the scenario is still valid today (it's a typical master-detail / n-to-1 scenario). On the other hand, the currently accepted solution is not only outdated but also a result of an xy-problem.
is there a more elegant way?
Absolutely. Take a look at this Flexible Column Layout tutorial: https://sdk.openui5.org/topic/c4de2df385174e58a689d9847c7553bd
No matter what control is used (App, SplitApp, or FlexibleColumnLayout), the concept is the same:
User clicks on an item from the master.
Get the binding context from the selected item by getBindingContext(/*modelName*/).
Pass only key(s) to the navTo parameters (no need to pass the whole item context).
In the "Detail" view:
Attach a handler to the patternMatched event of the navigated route in the Detail controller's onInit.
In the handler, create the corresponding key, by which the target entry can be uniquely identified, by accessing the event parameter arguments in which the passed key(s) are stored. In case of OData, use the API createKey.
With the created key, call bindObject with the path to the unique entry in order to propagate its context to the detail view.
The relative binding paths in the detail view can be then resolved every time when the detail page is viewed. As a bonus, this also enables deep link navigation or bookmark capability.
You can also set local json model to store your data, and use it in the corresponding views. But be sure to initialize or clear it in the right time.

How can I add sort items into a fragment dialog?

I have this fragment retrieve by this page https://openui5.hana.ondemand.com/explored.html#/sample/sap.m.sample.TableViewSettingsDialog/code
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<ViewSettingsDialog
confirm="handleConfirm" id='viewSettingsDialogId'>
<sortItems id="sortItemsId">
<!-- <ViewSettingsItem text="Product" key="Name" selected="true" />
<ViewSettingsItem text="Supplier" key="SupplierName" />
<ViewSettingsItem text="Weight" key="WeightMeasure" />
<ViewSettingsItem text="Price" key="Price" /> -->
</sortItems>
</ViewSettingsDialog>
</core:FragmentDefinition>
I want insert manually the sortItems in the control (or by data-binding in the xml-View).
How can I do it?
I try to do it by code in my controller:
//IF CLICK ON SETTINGS BUTTON
handleViewSettingsDialogButtonPressed: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("apps.appIntra.fragment.settingDialog", this);
}
// toggle compact style
jQuery.sap.syncStyleClass("sapUiSizeCompact", this.getView(), this._oDialog);
this._oDialog.open();
var element=sap.ui.getCore().byId("sortItemsId");
this.byId('sortItemsId').addSortItem(new sap.m.ViewSettingsItem({text:"field1", key:"Price"}));
this.byId('sortItemsId').addSortItem(new sap.m.ViewSettingsItem({text:"field2", key:"PLUTO"}));
},
But it not work...
I see this guide http://scn.sap.com/community/developer-center/front-end/blog/2014/02/20/sapui5-dialogwith-businesscard-as-xml-fragment-along-with-controller
but if I use
var element=sap.ui.getCore().byId("sortItemsId");
element value is undefined
The addSortItem is a method that works on the ViewSettingsDialog and not on sortItems.
Therefore, as you have already provided the id viewSettingsDialogId for the ViewSettingsDialog control, in you controller you can do the following,
var oViewSettingsDialog = sap.ui.getCore().byId("viewSettingsDialogId");
oViewSettingsDialog.addSortItem(new sap.m.ViewSettingsItem({text:"field1",
key:"Price"}));
/* and so on... */
This would add the sort items into the sortItems list.