ExstJs Store callback proper error handling - extjs

I want to receive a personalized error message when trying to load a store and my connection times out or the DB it's not accessible ... when I do an Ajax Request is very easy because I get "response" as a parameter either in success or failure ...
Ext.Ajax.request({
url: 'ajax_demo/sample.json',
success: function(response, opts) {
var obj = Ext.decode(response.responseText);
console.dir(obj);
},
failure: function(response, opts) {
console.log('server-side failure with status code ' + response.status);
}
});
But I'm facing problems to do the same when trying to load a store, I've defined a callback function but I only receive records, operation and success.
store.load({
callback : function(records, options, success) {
if (!success) {
// what can I do here to show personalized error sent from server
}
}
});
So, what is the proper way to handle a response like this to show it to the user?
{"success": false, "msg": "SomeExceptionFromServer"}
Best regards

When success is false operation never gets a response property instead it gets a getError method, but you should define a messageProperty in the proxyReader in order to work.
Example:
Ext.define("SC.store.SegurosCancelacionStore", {
extend: "Ext.data.Store",
model: "SC.model.PersonaSeguro",
proxy: {
timeout: 90000,
actionMethods: {
read : 'POST'
},
type: "ajax",
url: "../SegurosFinsolCancelacionServlet",
reader: {
type: "json",
root: "seguros",
messageProperty : 'msjError' //without this, it doesn't work
}
},
autoLoad: false
});
Implementation:
storeSegurosCancelacion.load({
params: {
'sucursal':sucursal,
'persona': persona
},
callback:function(records, operation, success){
msg.hide();
if(success == true){
if(records.length == 0){
Ext.Msg.alert('Resultado', 'No se ha encontrado informaciĆ³n');
}
}
if(success == false){
try{
Ext.Msg.alert('Error', operation.getError()); // way more elegant than ussing rawData etc ...
}catch(e){
Ext.Msg.alert('Error', 'Error inesperado en el servidor.');
}
}
}
});
Best regards

store.load({
callback : function(records, operation, success) {
console.log(operation.getResponse().msg);//this does the trick
}
});

If there is an error that you want to send, why not pass an empty data property along with the error message? So there are no records to load, but you get the message.
{
"success": false,
"msg": "SomeExceptionFromServer",
"data": {}
}
You might have to change data to your data property name.

Related

Backbone.js save always triggers error even on success

I've read several of the other posts about this problem and none of the solutions seem to be working for me. I have the following code in my View:
this.model.set({
username: $('#user-username').val(),
role: $('#user-role').val(),
description: $('#user-description').val()
});
this.model.save({ user_id: this.model.get('user_id')}, {
success: function(user, response) {
console.log('success:', response);
$('.flash-message').text("Success").show();
},
error: function(user, response) {
console.log('error:', response);
$('.flash-message').text(response.error).show();
}
});
and this on my server controller (nodejs running express 3):
UserController.prototype.updateAction = function(req, res) {
if (req.route.method != "put") {
res.send({status: "error", error: "update must be put action and must include values"});
return false;
}
var query = {'user_id': req.params.id};
var user = req.body;
var userRepository = this.userRepository
// delete _id to avoid errors
delete user._id;
userRepository.update(query, user, {}, function(err, updated) {
if ((err) || (!updated)) {
res.send({"status": "error", "error": err});
return false;
}
// send updated user back
util.log('updated user ' + user.user_id);
res.setHeader('Content-Type', 'application/json');
res.status(200);
res.send(JSON.stringify({"status": "success", "updated": updated}));
});
}
On save, my model is saved correctly in the server and I have verified the server response with this. So, as far as I can tell the server is returning status 200, valid JSON, with a valid JSON response header. And yet my backbone model.save function always triggers the error callback. Can anyone please tell me why and how to resolve this?
I am able to get this to work if set the dataType to text like so:
this.model.save({ user_id: this.model.get('user_id')}, {
dataType: "text",
success: function(user, response) {
console.log('success:', response);
$('.flash-message').text("Success").show();
},
error: function(user, response) {
console.log('error:', response);
$('.flash-message').text(response.error).show();
}
});
but doing so does not allow me to get the response back from the server. Instead I get this in the response var:
success: {
"_id": "5133b02062e15ed1d2000001",
}
Backbone expects to get back the model that it sent in its PUT or POST request body.
Instead of:
res.send(JSON.stringify({"status": "success", "updated": updated}));
Try this in your server's response:
res.json(user);
There may be a possibility that your call may have got in state 200 connection established which backbone detects as error, Backbone throws success only when the call is 200OK.
What's your server code? You need to make sure you're sending json back to backbone like so:
//In your express POST route
user.save(function(err) {
if(err){
console.log(err);
return res.json(401);
} else {
console.log('user: ' +user.username + ' saved');
return res.json(200);
}
Then in your backbone view you can check for the response and do what you need:
//some function in your view
this.model.save(this.formValues, {
success: function(model, response, options) {
if (response == 200) {
console.log('success :' + response);
//Do stuff
} else {
console.log('error: '+response);
//etc.
Also note that as per the backbone model documentation:
"save accepts success and error callbacks in the options hash, which will be passed the arguments (model, response, options)"

setMasked(false) not working

In my application i have used the Ext.Viewport.setMasked function, When i call the Processing mask showing properly.But not disabled when it reaches success.Here my code
{
Ext.Viewport.setMasked({
xtype: 'loadmask',
message: 'Processing...',
indicator: true
});
var data = Ext.JSON.encode(obj);
Ext.Ajax.request({
url: App.gvars.apiurl + 'AddItem', // url : this.getUrl(),
method: "POST",
params: data,
useDefaultXhrHeader: false,
withCredentials: true,
success: function (response) {
var respObj = Ext.JSON.decode(response.responseText);
if(respObj[0].response=="Success"){
Ext.Viewport.setMasked(false);
Ext.Msg.alert("Success", "A new wish has been added To Ur Wish List");
viewgiftlist();
goitems();
}
else{
Ext.Viewport.setMasked(false);
Ext.Msg.alert("Error",respObj[0].errorMsg);
}
},
failure: function (response)
{
Ext.Msg.alert("Error",response.responseText);
}});
}
Please help me to solve the issue
You didnt give setmask(false) in your failure message.. are you getting success response or failure?
The correct usage is in fact Ext.Viewport.setMasked(false);
My guess is that your success conditional isn't working properly. This should be an easy fix for you if you're using console! Fire up Chrome, hit F12 and use your console. Use a console.log after you decode your response, then you can properly debug this.
var respObj = Ext.JSON.decode(response.responseText);
console.log(respObj);
Also, not sure why the success portion of your response would be an array, usually a response looks something like this:
{"success":true,"data":[{"id":"1","first_name":"Test","last_name":"Test"}],"total":1}
Obviously you can craft them how you want, but just looks strange to me. With the above JSON the conditional would be like so:
if(respObj.success) {

How to get responsecode from store callback?

I'm trying to handle session timeout server-side.
When getting session timeout, my server sends back a response with json
{success: false}, ContentType: 'application/json', ResponseNo: 408
store:
var storeAssets = Ext.create('Ext.data.Store', {
model : 'modCombo',
autoLoad : false,
proxy : { limitParam : undefined,
startParam : undefined,
paramName : undefined,
pageParam : undefined,
noCache : false,
type : 'ajax',
url : '/caricaAssets.json',
reader : { root : 'data' }
}
});
And on the client side, I handle callback loading store like this:
storeAssets.load({
scope: this,
callback: function(records, operation, success) {
if (!success) { Ext.Msg.alert('Error'); }
}
});
To perform different responses, I'd like to change alert.
So, if response no. is 408, I can alert session expired (and so on, managing response numbers).
But I didn't find any way to get response no. in store callback!
Any suggestions?
Unfortunately, the callback method does not have the server response passed in as a parameter. This is likely since there are many ways to load data into a store, and not all of them will have a server response.
You can override the proxy's processResponse function to store the server's response with the operation object, then access it in your callback.
Ext.define('Ext.data.proxy.ServerOverride', {
override: 'Ext.data.proxy.Server',
processResponse: function (success, operation, request, response, callback, scope) {
operation.serverResponse = response;
this.callParent(arguments);
}
});
Then, to get the status:
storeAssets.load({
scope: this,
callback: function(records, operation, success) {
if (operation.serverResponse.status === 408) {
Ext.Msg.alert('Session expired');
}
}
});
I know this is quite old now, but I had a similar problem.
The solution I found was to listen for the exception event in the proxy.
proxy{
type: 'ajax',
reader: {
type: 'json'
,
listeners: {
exception: function(proxy, response, options){
Ext.MessageBox.alert('Error', response.status + ": " + response.statusText);
}
}
}
I also predicated my store load callback to only proceed when success is true.
Hopefully someone else searching will find this helpful.
Try this (on extjs 4.2.2)
callback: function (records, operation, success) {
operation.request.operation.response.status; }
Solved adding following code:
Ext.Ajax.on('requestexception', function(con, resp, op, e){
if (resp.status === 408) {
Ext.Msg.alert('Warning', 'Session expired');
} else {
if (resp.status === 404) {
Ext.Msg.alert('Error', Ext.util.Format.htmlEncode('Server not ready'));
} else {
if (resp.status !== undefined) {
Ext.Msg.alert('Error', Ext.util.Format.htmlEncode('Server not found (') + resp.status + ')');
} else {
Ext.Msg.alert('Error', 'Server not found');
}
}
}
});
When i call ajax request, server gives back information that are catched by this exception. Now I can handle callback code!
I know this question is pretty "old", but in ExtJS 4.2.2, when
callback: function(a,b,c) {}
happens, you can automatically catch the server response using
b.error.status
if (!c) {
if (b.error.status === 401) {
//logout
}
}
I don't know if it was implemented only a few time ago (after this was posted I mean), but still it can help anyone checking this post in the future I guess...

model.destroy() returning errors with sinatra backend

I'm having an issue getting my model.destroy method to work properly in backbone. This is my function
deleteEvent: function(){
var self = this;
var check = confirm("Are you sure you want to remove record " + this.model.get("ticket_id"));
if (check == true){
this.model.id = this.model.get('session_id');
this.model.destroy({
wait: true,
success: function(model, response, options){
console.log(options);
console.log(response);
self.$el.remove();
},
error: function(model, xhr, response){
console.log("ERROR:");
console.log(model);
console.log(xhr);
console.log(response);
}
});
}
else return;
},
The model looks like this:
vkapp.EventRecordModel = Backbone.Model.extend({
urlRoot: '/user_event',
idAttribute:"_id",
defaults: {
"ticket_id": '',
"start": '',
"end": ''
},
validate: function(attrib){ //This is only called when setting values for the model, not on instantiation
if (attrib.ticket_id == null)
alert("no ticket number");
if (attrib.start == undefined)
alert("no start time");
if (attrib.end == null)
alert("no end time");
if (attrib.start > attrib.end)
alert("start can't be before the end time.");
}
});
And this is what the route looks like in my sinatra.
delete '/user_event/:session_id' do
user_event = ProjectTimer.get(:session_id => params[:session_id])
user_event.destroy
end
I am not sure why I am getting an error return.
If you follow the advice from this answer
delete '/user_event/:session_id' do
user_event = ProjectTimer.get(:session_id => params[:session_id])
user_event.destroy
halt 204
rescue => e
# do something with the exception, maybe log it
halt 500
# or set status to 500 and re-raise
end
See Halting in the Sinatra docs for more.
I did get this working properly however by setting the dataType to "text." I found that Backbone.sync expects JSON of the object deleted to be returned in order to be successful. So, if we change the dataType to Text it over-rides that JSON expectancy. This is my final code
deleteEvent: function(){
var self = this;
var check = confirm("Are you sure you want to remove record " + this.model.get("ticket_id"));
if (check == true){
this.model.id = this.model.get('session_id');
this.model.destroy({
dataType: "text",
wait: true,
success: function(model, response, options){
self.$el.remove();
},
error: function(model, xhr, response){
console.log("ERROR:");
console.log(model);
console.log(xhr);
console.log(response);
}
});
}
else return;
},

Cancel store.remove after server call in ExtJS 4

I'm using ExtJS 4 and have an Ext.data.Store with an ajax proxy and api:
var gridStore = Ext.create('Ext.data.Store', {
autoSync: true,
proxy: {
type: 'ajax',
api: {
read: 'myurl',
create: 'myurl',
update: 'myurl',
destroy: 'myurl'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
},
writer: {
type: 'json',
writeAllFields: false,
root: 'data'
},
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show({
title: 'Server error',
msg: operation.getError(),
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
...
When I use the update function and my server returns a json object with success:false (because he entered something wrong) the field in my associated grid is still marked as changed and the user has the option to change his wrong value.
That works fine.
But when I remove a record from the store...
var store = Ext.StoreManager.lookup('gridStore');
store.remove(store.getById(id));
...then ExtJS removes this record from the store first and call the ajax api afterwards. So when the destroy api returns success:false the message is shown as exception like in the update api, thats fine, but my record has been removed from the store! As example the exception from the server says that you cannot remove this record because of whatever but it's already removed in the store.
How to cancel the store removement after the server sync? I want the record to stay in the store if the server returns success:false.
Any idea? Maybe a bug?
UPDATE SOLUTION
Based on Ryan's anwer, I modified the exception listener as following, which works very well:
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show(...);
// get the removed records and insert them where they have been
var removedRecords = gridStore.getRemovedRecords();
for(var i=0; i<removedRecords.length; i++){
var record = removedRecords[i];
gridStore.insert(record.index, record);
}
}
}
The insert technique didn't work for me, the removed record stays marked for removal on the next sync operation. I am using Ext.data.Store.rejectChanges() for this purpose.
Just extending the code you gave, specifically the listeners area:
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show({
title: 'Server error',
msg: operation.getError(),
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
gridStore.add(gridStore.getRemovedRecords());
}
}
I am useing callback functions 'success','failure' or 'callback' when sync.
I hope this method can help you.
store.remove(records);
store.sync({
success: function (proxy, operations) {
// pop success message
}, failure: function (proxy, operations) {
// resume records
store.rejectChanges();
}
});
I am using model.destroy, this is what i use for deleting singular entries from grid:
text : 'Delete',
itemId : 'delete',
scope : this,
handler : function() {
var selection = grid.getView().getSelectionModel().getSelection()[0];
if(selection) {
selection.destroy({
failure : function() {
console.log('Record could not be deleted.');
},
success : function() {
store.remove(selection);
console.log('Record successfuly removed.');
},
});
}
}

Resources