I am currently facing a strange behavior with my SAPUI5 coding when I do a DELETE with the model (sap.ui.model.odata.v2.ODataModel). I wanted to implement a list, which displays some "Favorites" in a SelectDialog. By pressing the icon, the users can delete a favorite. For the item itself I used a FeedListItem, which is triggering the iconPress-Event _handleIconPressDelete.
<FeedListItem icon="sap-icon://delete" iconActive="true" iconPress="_handleIconPressDelete" text="{Name}" sender="{ID}"/>
The event looks like this:
_handleIconPressDelete: function(oEvent) {
var oModel = oEvent.getSource().getModel();
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
}
But when this event is triggered, two identical delete requests are generated and causing an error, because with the current changeset coding in the backend, I am only allowed to do one request at the same time.
The strange thing is, this behavior only appears when I open the dialog the first. When I close and reopen it, everything works fine.
Do you have any ideas, what I might do wrong here so that two requests are generated? I also checked, if the event is triggered multiple times, but that wasn't the case.
As current workaround I am using deferredGroups as shown in the snipped below so that the two request are separated, but I think there must be better ways to solve this.
_handleIconPressDelete: function(oEvent) {
var oModel = oEvent.getSource().getModel();
oModel.setDeferredGroups(["group1"]);
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
groupId: "group1",
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
oModel.submitChanges({
groupId: "group1"
});
}
I too experienced the same issue where the event associated with iconPress of FeedListItem triggers twice though user click only once..
Following is a workaround which you can implement using custom coding.
Declare the following variable in view controller's onInit()
this._bFirstTrigger = true;//SETTING FOR THE FIRIST TIME
Use this in FeedListItem's iconPress event to ensure that the relevant code executes only once as follows:
_handleIconPressDelete: function(oEvent) {
if (this._bFirstTrigger) {
var oModel = oEvent.getSource().getModel();oModel.setDeferredGroups(["group1"]);
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
groupId: "group1",
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
oModel.submitChanges({
groupId: "group1"
});
}
this._bFirstTrigger = false;
}
else{
this._bFirstTrigger = true;
}
Regards,
Fahad Hamsa
Related
I want to create some events on my DayPilot scheduler using my own modal window. It works fine when creating the first event. After that the event gets doubled, tripled and so on along with the new created events. It seems like args is not getting empty.
Here is my code for onTimeRangeSelected:
dp.onTimeRangeSelected = function (args) {
$('#myModal').show();
$('.schedule_option').click(function(){
$('#myModal').hide();
dp.clearSelection();
var txt=this.getAttribute("arg");
var colour = this.style.backgroundColor;
var e = new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: txt,
backColor:colour
});
var params = {
start: args.start,
end: args.end,
text: txt,
backColor:colour,
id:DayPilot.guid(),
resource: args.resource
};
DayPilot.Http.ajax({
url: "admin/backend_create.php",
method: "POST",
data: params,
success: function(ajax) {
dp.message("succes");
},
error: function(ajax) {
dp.message("Saving failed");
}
});
dp.events.add(e);
});
}
And the effect I am getting is:
The modal contains 3 buttons corresponding to each event to be scheduled type.
If I use DayPilot modal I don't have issues. I can't find an explanation because somehow it worked until last Friday.
Thanks!
Ok. I found the issue. It's the click event handler that it is being added every time a time range it's selected. I will have to get it out of there and find another way to use my own modal.
I'm using ag-grid (javascript) to display a large amount of rows (about 3,000 or more) and allow the user to enter values and it should auto-save them as the user goes along. My current strategy is after detecting that a user makes a change to save the data for that row.
The problem I'm running into is detecting and getting the correct values after the user enters a value. The onCellKeyPress event doesn't get fired for Backaspace or Paste. However if I attach events directly to DOM fields to catch key presses, I don't know how to know what data the value is associated with. Can I use getDisplayedRowAtIndex or such to be able to reliably do this reliably? What is a good way to implement this?
EDIT: Additional detail
My current approach is to capture onCellEditingStopped and then getting the data from the event using event.data[event.column.colId]. Since I only get this event when the user moves to a different cell and not just if they finish typing I also handle the onCellKeyPress and get the data from event.event.target (since there is no event.data when handling this event). Here is where I run into a hard-to-reproduce problem that event.event.target is sometimes undefined.
I also looked at using forEachLeafNode method but it returns an error saying it isn't supported when using infinite row model. If I don't use infinite mode the load time is slow.
It looks like you can bind to the onCellKeyDown event. This is sometimes undefined because on first keydown the edit of agGrid will switch from the cell content to the cell editor. You can wrap this around to check if there is a cell value or cell textContent.
function onCellKeyDown(e) {
console.log('onCellKeyDown', e);
if(e.event.target.value) console.log(e.event.target.value)
else console.log(e.event.target.textContent)
}
See https://plnkr.co/edit/XhpVlMl7Jrr7QT4ftTAi?p=preview
As been pointed out in comments, onCellValueChanged might work, however
After a cell has been changed with default editing (i.e. not your own custom cell renderer), the cellValueChanged event is fired.
var gridOptions = {
rowData: null,
columnDefs: columnDefs,
defaultColDef: {
editable: true, // using default editor
width: 100
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted', event);
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped', event);
},
onCellValueChanged: function(event) {
console.log('cellValueChanged', event);
}
};
another option could be to craft your own editor and inject it into cells:
function MyCellEditor () {}
// gets called once before the renderer is used
MyCellEditor.prototype.init = function(params) {
this.eInput = document.createElement('input');
this.eInput.value = params.value;
console.log(params.charPress); // the string that started the edit, eg 'a' if letter a was pressed, or 'A' if shift + letter a
this.eInput.onkeypress = (e) => {console.log(e);} // check your keypress here
};
// gets called once when grid ready to insert the element
MyCellEditor.prototype.getGui = function() {
return this.eInput;
};
// focus and select can be done after the gui is attached
MyCellEditor.prototype.afterGuiAttached = function() {
this.eInput.focus();
this.eInput.select();
};
MyCellEditor.prototype.onKeyDown = (e) => console.log(e);
// returns the new value after editing
MyCellEditor.prototype.getValue = function() {
return this.eInput.value;
};
//// then, register it with your grid:
var gridOptions = {
rowData: null,
columnDefs: columnDefs,
components: {
myEditor: MyCellEditor,
},
defaultColDef: {
editable: true,
cellEditor: 'myEditor',
width: 100
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted', event);
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped', event);
}
};
I'm trying to use Canada Post's Address Complete on my form as such
var fields = [
{ element: "street_address", field: "Line1" },
{ element: "city_address", field: "City", mode: pca.fieldMode.POPULATE },
{ element: "postal_code", field: "PostalCode", mode: pca.fieldMode.POPULATE },
{ element: "country", field: "CountryName", mode: pca.fieldMode.COUNTRY }
],
options = {key: KEY},
control = new pca.Address(fields, options);
addressComplete.listen('load', function(control) {
control.listen("populate", function (address) {
if(address.ProvinceCode == "ON"){
console.log("ONTARIO");
document.getElementById('province').selectedIndex = 2;
}
else if(address.ProvinceCode == "QC"){
document.getElementById('province').selectedIndex = 3;
}
});
});
I'm able to search for an address and have some fields auto populate. The Province on my form is a dropdown which is where I want to use the listener as suggested in the website, but it doesn't work? Could someone please let me know what I'm doing wrong?
I tried playing with the API and I couldn't get any events to fire on the addressComplete object but the ready event. However, since we have all ready constructed a control instance, I just removed the load listener and attached the populate event handler directly to the control object we constructed. This seemed to work.
//addressComplete.listen('load', function (control) {
control.listen('populate', function (address) {
// TODO: Handle populated address here.
});
//});
I got error - Uncaught ReferenceError: control is not defined
Once the Canada Post JavaScript is loaded, then the control instance is created - addressComplete.controls[0]
To listen to populate event of the control:
addressComplete.controls[0].listen("populate", function (address) {
// TODO: Handle populated address here.
});
load() and reload() apis are also available.
addressComplete.controls[0].load();
addressComplete.controls[0].reload();
// destroy();
// load();
I'm super confused by my code. Let me show what it looks like:
$(document).ready(function ($) {
var customer_exists = false;
$.get(window.additional_parameters.customer_exists_url, "json")
.done(function () {
customer_exists = true;
})
.always(function () {
// Don't make request to buy clickable until we know if the customer exists
$('#request-to-buy').on('click', function(e) {
request_to_buy(customer_exists);
});
});
function request_to_buy(customer_exists) {
response = can_request_to_buy();
response.done(function (response) {
if (customer_exists) {
// Actually create the request on the server
$.post(window.additional_parameters.request_to_buy_url,
{'ticket_id': window.additional_parameters.ticket_id},
"json")
.done(function (response) {
request_to_buy_success(response);
})
.fail(function () {
var message = handle_ajax_response(response);
show_ajax_message(message);
});
} else {
show_pre_stripe_popup();
}
})
.fail(function (response) {
var error_message = handle_ajax_response(response);
show_ajax_message(error_message, 'danger');
});
}
$(document).ready(), we set a variable called customer_exists. This variable guides the path of the code afterwards and is pretty important. If the $.get AJAX request is successful, it's true, otherwise it remains it default value of false. After the AJAX response, we attach a click event to "#request-to-buy." My goal here is to create a closure and pass in the value of customer_exists that was just set. This doesn't happen.
A good portion of the time ( I had it work correctly once or twice ), when I inspect request_to_buy in the debugger, I can see that customer_exists is a jQuery click event. why ??? Shouldn't it take on the value of the customer_exists from the surrounding scope of where the function was created? Can anyone explain what is going on here?
Thank you
EDIT: Here's a little more information that describes how it works sometimes...
The first time that I click '#request-to-buy', the handler is
function(e) {
request_to_buy(customer_exists);
}
This is what we would expect. e contains the click event, customer_exists retains it's value, and everything works inside request_to_buy.
Every time I click '#request-to-buy' after the first, instead of the above function being called, request_to_buy is called directly, and instead of passing in customer_exists in the first parameter, the click event is passed in instead. I hope this helps someone.
You should be able to do this without the need for the cumbersome outer var customer_exists.
For example :
$(document).ready(function ($) {
$.get(window.additional_parameters.customer_exists_url, "json").then(function () {
// Don't make request to buy clickable until we know if the customer exists
$('#request-to-buy').on('click', request_to_buy);
}, function() {
$('#request-to-buy').on('click', show_pre_stripe_popup);
});
function request_to_buy(e) {
e.preventDefault();
can_request_to_buy().then(function(response) {
// Actually create the request on the server
$.post(window.additional_parameters.request_to_buy_url, {
'ticket_id': window.additional_parameters.ticket_id
}, "json").then(request_to_buy_success, function() {
show_ajax_message(handle_ajax_response(response));
});
}).fail(function(response) {
show_ajax_message(handle_ajax_response(response), 'danger');
});
}
}
show_pre_stripe_popup will also be passed an event and you may need to do e.preventDefault(); there too.
You will need to check that the correct parameters are passed to the various error handlers. I can't verify them.
If it still doesn't work, then you must suspect other code that's not included in the question, for example the function can_request_to_buy().
var customer_exists = false;
Declare this outside of ready block.
I sign the form on the 3 events but the event 'actioncomplete' not fired. The other two events are executed, success callback is performed.
if(obj instanceof Ext.form.BasicForm){
var before='beforeaction';
var complete='actioncomplete';
var error='actionfailed';
}
obj.addListener( before,
function(o,e) {
this.changeSysState(lang[msg_id].loading,'loading','load_N_'+this.load_id);
}, this);
obj.addListener( complete,
function(o,e) {
this.showStatus(lang[msg_id].complete,'complete','load_N_'+this.load_id);
obj.addListener( error,
function(o,e) {
this.changeSysState(lang[msg_id].error,'error','load_N_'+this.load_id);
}, this);
My form
var changePanel = new Ext.form.FormPanel({
labelWidth : 132,
layout : 'form',
border : false,
defaults:{allowBlank:false,width:165},
url : '/xstore/xstore_change_pass.php',
items : [ /*some fields*/ ]
});
submit call
var form = changePanel.getForm();
form.submit({
success: function(r,o) {
winPass.destroy();
}
});
Server returns
{"success":true}
Use ExtJs 3.4
With that response, actioncomplete should be fired. You should search for reasons why it isn't. I see some issues with your code that might causing this behaviour:
you declared before, complete, error as local variables and you use them out of scope; this might be an issue; use that instead:
if(obj instanceof Ext.form.BasicForm){
var before='beforeaction';
var complete='actioncomplete';
var error='actionfailed';
obj.addListener( before, function(o,e) {
this.changeSysState(lang[msg_id].loading,'loading','load_N_'+this.load_id);
}, this);
obj.addListener( complete, function(o,e) {
this.showStatus(lang[msg_id].complete,'complete','load_N_'+this.load_id);
}, this);
obj.addListener( error, function(o,e) {
this.changeSysState(lang[msg_id].error,'error','load_N_'+this.load_id);
}, this);
}
you destroy something in success callback; this success callback is fired before actioncomplete, so if it also destroys form it might be another issue
If neither of those help, check in developer tools for server response. There might be some error code returned which also might be the reason.
Here is fiddle where you can see the second problem (actioncomplete not firing after destroying form).