Bind an Event via a JSON Model - javascript

I wonder if it is possible to bind an Event via a JSONModel.
If I do so, it will always throw this Exception:
Uncaught TypeError: I.fFunction.call is not a function
This is my code:
_ViewReference: undefined,
_oMenuItemsConfigModel: undefined,
createMenu: function(oItem){
if (!this._menu) {
this._menu = new sap.ui.unified.Menu(this._oMenuConfig);
this._menu.setModel(this._oMenuItemsConfigModel);
this._menu.bindAggregation("items", "/", new sap.ui.unified.MenuItem({
text: "{text}",
icon: "{icon}",
select: "{select}",
enabled: "{enabled}"
}));
this._ViewReference.addDependent(this._menu);
}
var eDock = sap.ui.core.Popup.Dock;
this._menu.open(false, oItem, eDock.BeginTop, eDock.BeginBottom, oItem);
return oItem;
}
I have a Universal ContextMenu which just needs some config in order to get created. This is how I call this function from my Controller:
var oContextMenu = new ContextMenu(this.getView(),
new sap.ui.model.json.JSONModel(
[
{
text: "Copy",
select: [this.onContextMenuItemCopySelected, this]
},
{
text: "Paste",
select: [this.onContextMenuItemPasteSelected, this]
}
]
)
);
Here is a JSBin Example.

You cannot use databinding for events.
But you can implement an universal event handler for your menuitems that will call the appropriate function.
Bind the menu items select event to a common event handler:
this._menu.bindAggregation("items", "/", new sap.ui.unified.MenuItem({
text: "{text}",
select: [this.onSelect, this]
}));
And implement the handler like this:
onSelect:function(oEvent){
var item = oEvent.getParameter("item");
var context = item.getBindingContext();
var fnConfig = context.getProperty("select");
fnConfig[0].bind(fnConfig[1])();
}
fnConfig is the Array of function an this-object from the model.
Using Function.bind() lets you call the function on the given this object.
Here it is on JSBin

Related

Dynamically create UI elements via Controller and bind them to a specific properties in Model

I've been trying to add dynamic content to my dialog based on specific object in my JSONmodel, which is an array of objects.
My model has the following structure, I've set it like this(dummy data):
Note: I have multiple models active in this controller's view, each of which has its own model data.
this.setData( emp: [
{
col1: "1.4",
col2: "2.0",
col3: "3.1"
},
{
col1: "4.1",
col2: "5.3",
col3: "6.5"
}
]);
So I've set the model data successfully and now I am able to access it via:
var modelData= this.oView.getModel("myModel").oData;
What I want now is to dynamically create sap.m.Dialog and dynamically fill it with multiple sap.m.Input elements which have values based on a single object from my model:
var getDialogContent = function(modelData){
var arr = [];
var keys = Object.keys(modelData[0]); // property names. I hard-coded first obj for test.
// I want to use these properties and bind a new input on dialog for each property.
jQuery.each(keys, function(i, key) {
// 'myModel>/emp/0/'+key is a supposed full path to property...
// according to this link:
// https://sapui5.hana.ondemand.com/1.36.6/docs/guide/91f0ed206f4d1014b6dd926db0e91070.html
newInput.bindProperty("value", 'myModel>/emp/0/' + key); //key is col1 the first time
newInput.setProperty("description", key);
newInput.setProperty("type", sap.m.InputType.Number);
arr.push(newInput);
});
return arr;
};
I call getDialogContent() in the content property of the dialog to set its content.
Now, everything works save for the binding newInput.bindProperty("value", 'myModel>/emp/0/' + key);, the input fields that are displayed are just empty and show no sign of binding, also newInput.getBindingContext("myModel"); returns undefined.
var dialog = new sap.m.Dialog({
title: 'Dynamic dialog: ',
type: 'Message',
content: getDialogContent(modelData),
buttons: new sap.m.Button({
text: 'Cancel',
press: function () {
dialog.close();
}
}),
afterClose: function() {
dialog.destroy();
}
});
Does anyone have any idea what is wrong here and why can't I bind my property to the input element? I basically just want to bind values of my dynamic input fields to arbitrary object from object array in my JSON Model. Any suggestion is welcome.
Edit(Solution):
On the var keys = Object.keys(modelData[0]); line I replaced modelData[0] with modelData["emp"][0] as I was accessing specific object form JSONModel. Now it works.
Did you add the dialog to the dependents of your view? When I remove that step in our app, the result is exactly as you described: The fields are empty and getBindingContext() returns undefined.
One of the best way to implement a dialog in a reusable manner is the one described in this link. You have to add the dialog as dependent to the "parent" view in order to retrieve the models set on that view.
onDialogOpen: function () {
if (!this.oDialog) {
this.oDialog = new sap.m.Dialog({
title: 'Dynamic dialog: ',
type: 'Message',
content: getDialogContent(modelData),
buttons: new sap.m.Button({
text: 'Cancel',
press: function () {
this.oDialog.close();
}.bind(this)
}),
afterClose: function() {
this.oDialog.destroy();
}.bind(this)
});
//to get access to the view models
this.getView().addDependent(this.oDialog);
}
this.oDialog.open();
},

ExtJS: Field change event fires before ViewController's init

I have a field that is stateful, and I also have it hooked up to the change event... when its value changes, I want to perform some operation. However, because it's a stateful field, the change event fires when I go back to this view, and unfortunately, the change event fires before the ViewController's init method, which means I will not be able to access my reference lookup.
In the following example, run it, change the date, and then re-run the application... you'll see a console.log that appears for the change, and then for the init. I realize I could set up the handler in the init method, but that just seems silly. I also realize I could create myField as a private var and access it that way, but that also seems silly. And yes, I could change to the select event, but that's not what I want to do. Anyone have any thoughts? Here's an example:
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myView',
init: function() {
console.log('init fired', this.lookupReference('myField'))
},
onChange: function(value) {
console.log('onChange fired', this.lookupReference('myField'));
}
});
Ext.define('MyView', {
extend: 'Ext.container.Container',
controller: 'myView',
renderTo: Ext.getBody(),
items: [{
xtype: 'datefield',
value: new Date(),
stateful: true,
stateId: 'blahblah',
listeners:{
change: 'onChange'
}
}, {
xtype: 'datefield',
value: new Date(),
reference: 'myField'
}]
});
Ext.create('MyView');
}
});
This is because the state mixin is initialized before the controller, this is code taken directly from Ext.Component's constructor:
me.mixins.state.constructor.call(me);
me.addStateEvents('resize');
controller = me.getController();
if (controller) {
controller.init(me);
}
There is no config to change this behavior. Honestly, I've never seen someone make a form field's value stateful.
You can use the buffer config to delay event firing.
This has an advantage of setting up the event after the controller is initialised.
The solution:
listeners: {
change: {
buffer: 300,
fn: 'onChange'
}
}
An Alternative is to handle 'beforestaterestore` event of the stateful field and apply the state value only after controller is initialised.
listeners: {
beforestaterestore: function (field, state){
var controller = field.up().getController();
Ext.Function.interceptAfter(controller, 'init', function(){
field.setValue(state.value); // update
},this);
return false;
}
}

calling wrong method ExtJS

I have this piece of code :
listeners:
{
beforerender: function()
{
if (importButton == true)
{
this.menu.add(
{
text: 'Import',
iconCls: 'importIcon',
listeners:
{
click: new ifAuthorizing('import')
}
})
}
this.menu.add(
{
text: 'Consultation',
iconCls: 'searchIcon',
listeners:
{
click: menuConsultation
}
})
}
}
that is supposed to add items to a menu when some conditions are OK.
It's working, the button are well added if the conditions matches.
The problem is coming from the
listeners:
{
click: new ifAuthorizing('import')
}
This listener is supposed to be appended to the menu item, but it is triggered during the beforerender event of its parent.
function ifAuthorizing(arg) {
console.log('import')
}
The 'import' is displayed in the console logs during the beforerender event, and then if I click on the menu item that is supposed to have a click method, nothing happens.
I would like to know why.
new ifAuthorizing('import')
Here operator new tries to create an object and interpretes ifAuthorizing as a constructor. The constructor is called immediately, that's why it fires on beforeRender event. As a result you get some object which is not a copy of function ifAuthorizing, so it can't be called on menu item's event with the desired result.

Variable in javascript file not accessible in object property

I have the following problem. I have a JS-file which has a handful of variables. Those I initialize in a function:
var currentYear;
var previousYear;
var urlQuarterDates;
var urlHalfYear;
var urlYear;
var urlMonth;
var urlProposalsSentAndReceived; //= '/Marketing/ListProposalsSentAndReceived';
var urlProposalsResponsibleMonth;
function initTabReportProposalsMonth(_currentYear, _previousYear, _urlViewProposal,
_urlQuarterDates, _urlHalfYear, _urlYear, _urlMonth, _urlProposalsSentAndReceived,
_urlProposalsResponsibleMonth) {
currentYear = _currentYear;
previousYear = _previousYear;
urlQuarterDates = _urlQuarterDates;
urlHalfYear = _urlHalfYear;
urlYear = _urlYear;
urlMonth = _urlMonth;
urlProposalsSentAndReceived = _urlProposalsSentAndReceived;
urlProposalsResponsibleMonth = _urlProposalsResponsibleMonth;
}
I have defined an event handler in the same JS-file:
function onPeriodSelect(combo, rec, i) {
var conn = new Ext.data.Connection();
var params = { };
switch(rec.get('myId'))
{
case _currentQuarter1:
conn.url = urlQuarterDates;
params.y = currentYear;
params.index = 1;
break;
}
reload(); //
}
The variables urlQuarterDates and currentYear are readily accessible. So far, so good...
I also have an ExtJs Grid with a data store which is declared inline:
var gridSentAndReceived = new Ext.grid.GridPanel({
title: 'Totaal',
autoHeight: true,
autoWidth: true,
store: new Ext.data.Store({
id: 'idStoreSentAndReceived',
proxy: new Ext.data.HttpProxy({ url: urlProposalsSentAndReceived,
timeout: 1800000 }),
reader: new Ext.data.JsonReader(
{
root: 'rows'
},
[
{ name: 'Status' },
{ name: 'nrOfProposals' },
{ name: 'TotalRevenueHardware' },
{ name: 'TotalRevenueYearly' },
{ name: 'TotalRevenueHours' }
]),
remoteSort: false
}),
frame: true,
iconCls: 'icon-grid',
columns: [
...
],
viewConfig: {
forceFit: true
}
});
The reload() function calls the load of the store of gridSentAndReceived. This generates an exception: the url is not defined at all. If I initialize the url right at its declaration (which is currently commented out' it works fine. When I browse using the debugger it shows that urlProposalsSentAndReceived is initialized. Still, it claims there is no URL.
This seems to be a scope problem, since variables are accessible from the event handler but obviously not elsewhere. Anybody knows how to fix it? The URLs are created using server tags and those cannot be put in JS files. I wouldn't enjoy putting them directly in the JS file as a text string. Is there a possible solution?
Update
I have tried a few more things but nothing works.
I have tried:
'beforeload': function (store, options) {
store.proxy.setUrl('/Marketing/ListProposalsSentAndReceived');
}
but even that didn't work. Still got the same exception. I really have no clue why that failed though, I took the code from the ExtJs Documentation under 'api'.
Now I have no choice but hardcoding the urls in my js-file though I'd very much prefer to use servertags and add them dynamically. Hopefully, one day, I'll find a solution rather than getting runtime errors when I change the location of a controller action.
This is not a scope issue. At the time you run your code urlProposalsSentAndReceived is not defined. If you set that variable via an event handler, the value is always set after gridSentAndReceived is initialized.

Ext.Button handler config option

someClass = Ext.extend(someClassB, {
_someFunctionC{
someButton = new Ext.button({
handler: function () {
this._onClick('click');
}
}),
_onClick(someMessage){
Ext.Msg.alert(someMessage);
}
}
}
_onClick eats one parameter; in the above code you put in the 'click' event because you want _onClick to be executed after the user clicks on the button. However, how do you specify this specific 'click' registration AND pass in a local variable as the _onClick parameter at the same time?
As an aside, why do you even have to specify 'click', when the API states that handler always pertains to a click? Is this additional information not unnecessary?
Typically you set it up like this. No real need to pass parameters since someFunction is a member of your 'class' and has access to any data you'd want.
var button = new Ext.Button({
handler: this.someFunction
scope: this
});
someFunction: function() {
// do something interesting.
}
So if i understand correctly you want to set the handler config option but set the arguments yourself in one go?
Does this do what you want?
// clicking the button alerts 'Hello World'
new Ext.Button({
text: 'Test',
handler: function(value){
alert('Hello, ' + value);
}.createCallback('World')
});
Notice the createCallback executed on the anonymous function, this creates a callback function for handler which only gets passed the arguments you pass to createCallback.
Another way that I've found to do this is to pass a custom config option along with your button. Say you wanted to have a splitbutton that could choose the amount of banners to add. (this is from a recent project)
{
xtype: 'splitbutton',
iconCls: 'icon addBanners',
ref: '../addBanner',
text: 'Add Banner',
menu: new Ext.menu.Menu({
items: [{
text: 'Add 10 Banners',
scope: this,
handler: this.addBanner,
numBanners: 10
},{
text: 'Add 20 Banners',
scope: this,
handler: this.addBanner,
numBanners: 20
}]
}),
scope: this,
handler: this.addBanner,
numBanners: 1
}
And in your function:
addBanner: function(button, event) {
if (button.numBanners) {
// do whatever
}
}
You can also create a callback function that inserts extra parameters when it is called:
var button = new Ext.Button({
handler: this.someFunction.createDelegate(button,['Some message'])
});

Categories

Resources