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).
Related
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
I have defined a general ajaxComplete function to be triggered after each ajax request as follow;
$(document).on('pfAjaxComplete', function(event, xhr, options) {
doStuff();
});
Note I use pfAjaxComplete instead of ajaxComplete to work under PrimeFaces
Now, on each ajaxcomplete the 'doStuff()' function is being called. Problem is that inside the 'doStuff()' function I trigger several ajax calls to be executed based on a PrimeFaces remoteCommand
function doStuff() {
var elements = $('#wrapper').find("[id*='elements']");
if (elements !== null && elements .length > 0) {
$.each(elements , function(index) {
newAjaxCallBack([{name: 'param', value: 'val'}]);
});
}
}
My remote command
<p:remoteCommand name="newAjaxCallBack" actionListener="#{backingBean.action}" />
This is working fine, the backing bean action method is being called. Problem is the new ajax callback on 'doStuff' triggers a new ajaxOnComplete event, which makes sense of course but then it gets me on an infinite loop. Had tried to work it out but couldn't find any solution to it.
Any ideas or suggestions? Would there be a way to send a parameter on the newAjaxCallBack and then detecting it on the ajaxComplete function so as to avoid the doStuff call? Thanks!
A quick solution would be to append additional parameters to your calls on which you don't want another action to be executed.
If you choose a "paremeter" that is not used on the target page, it won't care about it.
$(document).ajaxComplete(function(event, xhr, options) {
if (options.url.indexOf("noStuff=true") != -1){
alert("Ignoring Ajax Request from noStuff");
return;
}
doStuff();
});
function doStuff(){
alert("doing stuff");
//another fake ajax request
$.ajax({
url: "http://google.de",
data: [
{name: 'param', value: 'val'},
{name: 'noStuff', value: 'true'}
]
});
}
http://jsfiddle.net/3kwhn0kx/
Finally got it working, solution was similar to #dognose one, in my case I had to use the data primefaces handled, this way;
...
$.each(elements , function(index) {
newAjaxCallBack([{name: 'dontcall', value: 'true'}]);
});
...
and then:
$(document).on('pfAjaxComplete', function(event, xhr, options) {
if (xhr.pfSettings.data.indexOf('dontcall') !== -1) {
return;
}
doStuff();
});
As you may see, I'm using the pfSettings object, which has an attribute 'data' that is basically a string with info related to the request including params, so if parameter present, don't do anything.
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 need to notify the user of my webpage what a model.save() call returned from the server.
The situation is so that a click triggers getAnswer() in the view, which in turns triggers the models method getAnswerServer(), which invokes .save() on the model, which is passed a success callback that accesses the response.
But how do I notify the user (by using a view event, or anything else) what the response was?
Here's what I have in my view:
events: {"click button#active" : 'getAnswer'},
initialize: ...
render: ...
getAnswer: function() {
this.model.getAnswerFromServer();
},
The model:
getAnswerFromServer: function() {
this.save({myAnswer1 : false}, {success: function(model, response) {
answer = model.get('answer');
if (answer === true) {
console.log("The answer is true! But how do I tell the user about it?");
}
else if (answer === false) {
console.log("The answer is false! But again, how do I tell this to my user?");
}
}});
}
Ideally I would like to be able to call an event handler here and trigger an event in a different view, but I can't figure out how to access it from the success callback? Even triggering a custom event for this view would be enough actually.
Thanks!
You can always use a sync event on the view which you would like to show
option 1
// The view that you would like to show
initialize: function() {
this.listenTo(this.model, 'sync', this.render);
}
This will be called when the model that is being saved syncs with the server.
But if the view that you wuld like to show does not contain this model , then you can always define a custom event handler and trigger this is the callback..
option 2
var customEvents = _.extend({}, Backbone.Events);
this.save({
myAnswer1: false
}, {
success: function (model, response) {
answer = model.get('answer');
customEvents.trigger('notifyAnswer', { isAnswer : answer });
}
});
// In the view where you want to show
initialize: function() {
this.listenTo(customEvents, 'notifyAnswer', this.notify);
},
notify: function(data) {
if(data.isAnswer) {
// Show something
}
else {
// show something else
}
}
model.save returns a promise. You can capture it in your getAnswer method and trigger an event or handle your error:
getAnswer: function() {
var self = this;
var p = this.model.save();
p.done(function(data, status) {
answer = self.model.get('answer');
if (answer === true) {
self.doTrueThing();
}
else if (answer === false) {
self.doFalseThing();
}
});
p.fail(function() {
// failed :(
});
},
Is there a reason why you are not just doing:
getAnswer: function() {
this.model.save({myAnswer1 : false}, {success: function() {
...
}.bind(this)});
},
in the view itself, rather than using a function defined in the model?
Then, you could bind the success handler to the view (with .bind(this), as above) and then refer to the view in the success handler as this. You could then call any function defined in the view to update the view, like this.updateMe().
I'm writing a simple message board app to learn backbone. It's going ok (a lot of the use of this isn't making sense) but am a little stuck in terms of how I would remove a form / html from the dom. I have included most of the code but you can see about 4 lines up from the bottom, the part that isn't working. How would I remove this from the DOM?
thx in advance
var MbForm=Backbone.View.extend({
events: {
'click button.add-new-post': 'savePost'
},
el: $('#detail'),
template:_.template($('#post-add-edit-tmpl').html()),
render: function(){
var compiled_template = this.template();
this.$el.html(compiled_template);
return this;
},
savePost: function(e){
//var self=this;
//console.log("I want you to say Hello!");
data={
header: $('#post_header').val(),
detail: $('#post_detail').val(),
forum_id: $('#forum_id').val(),
post_id: $('#post_id').val(),
parent_id: $('#parent_id').val()
};
this.model.save(data, {
success: function(){
alert('this saved');
//$(this.el).html('this is what i want');
this.$el.remove();// <- this is the part that isn't working
/* none of these worked - error Uncaught TypeError: Cannot call method 'unbind' of undefined
this.$el.unbind();
this.$el.empty();
this.el.unbind();
this.el.empty();
*/
//this.unbind();
//self.append('this is appended');
}
});
Backbone doesn't call the success callback with any particular this, it is simply called as a plain function. So, this inside your success callback will be window rather than the view you're expecting it to be.
Any of the usual solutions will work:
Save the desired this in a local variable:
var _this = this;
this.model.save(data, {
success: function() {
//...
_this.remove();
Use a bound function:
this.model.save(data, {
success: _(function() {
//...
this.remove();
}).bind(this)
Use a named bound function:
initialize: function() {
_.bindAll(this, 'save_success');
}
//...
this.model.save(data, {
success: this.save_success
And the usual variations on the above.
Also note that I switched to View#remove since you are apparently trying to remove the whole view and that's the usual way to do it.