I am trying to add optimistic updates to my Flux model. I am smushing the UI action dispatch and the server action dispatch into one action. My code in the action creator looks like this:
deleteItem: function(itemId) {
// optimistic update
WebshipDispatcher.handleServerAction({
type: ActionTypes.DELETE_ITEM,
deleteStatus: 'success',
itemId: itemId
});
// now let's actually check if that was the correct result
AppAjaxUtil.get('/deleteItem', {itemId: itemId}, function(result) {
WebshipDispatcher.handleServerAction({
type: ActionTypes.DELETE_ITEM,
deleteStatus: result.status, // 'success' or 'failure'
itemId: itemId
});
}, function(error) {
WebshipDispatcher.handleServerAction({
type: ActionTypes.DELETE_ITEM,
error: error
});
});
}
Is this an appropriate way to allow for optimistic updates or am I thinking about this piece incorrectly?
#fisherwebdev is right. The true logic would happen in your store. For example how would you handle the logic when an item does fail to delete? That becomes a beast on it's own. You don't really want to remove the item from your store unless you have confirmation from the server. A library like Ext marks the record as dirty while it waits for a successful response from the server. So the update is still happening optimistically, but the user and the record are notified if the server fails.
So you could have a collection of dirty records in your store that are removed when your server responds with success. This is rough, but something like the following:
deleteItem: function(itemId) {
// optimistic update
WebshipDispatcher.handleServerAction({
type: ActionTypes.MARK_ITEM_AS_DIRTY,
deleteStatus: 'success',
itemId: itemId
});
// now let's actually check if that was the correct result
AppAjaxUtil.get('/deleteItem', {itemId: itemId}, function(result) {
WebshipDispatcher.handleServerAction({
type: result.status ? ActionTypes.DELETE_ITEM : ActionTypes.DELETE_ITEM_FAIL,
deleteStatus: result.status, // 'success' or 'failure'
itemId: itemId
});
}, function(error) {
WebshipDispatcher.handleServerAction({
type: ActionTypes.DELETE_ITEM_FAIL,
error: error,
itemId: itemId
});
});
}
So basically you remove the dirty record from your store if you your response is successful. Otherwise you have a reference of your dirty records in your store that can try again with your server behind the scenes while your app is still running. So in essence your user does not have to sit and wait for a response.
Related
The below code has both success and failure handling
jQuery.ajax({
type:"post",
dataType:"json",
url: myAjax.ajaxurl,
data: {action: 'submit_data', info: info},
success: function(data) {
successmessage = 'Data was succesfully captured';
$("label#successmessage").text(successmessage);
},
error: function(data) {
successmessage = 'Error';
$("label#successmessage").text(successmessage);
},
});
$(":input").val('');
return false;
However we are not following the above...We are following as below
jQuery.ajax({
type:"post",
dataType:"json",
url: myAjax.ajaxurl,
data: {action: 'submit_data', info: info},
success: function(data) {
if(data.responseType == 'success') {
// success
}
if(data.responseType == 'failure') {
// failure
}
}
});
$(":input").val('');
return false;
Is our approach is the correct or wrong approach ??
Basically every response will be success and show error message based on the response type
Please advise. We need to follow the best practice
data. responseType doesn't return a 'success' or 'failure'. It is contains an enumerated value which represents the type of response coming back to you like text, json, arrayBuffer etc..
Hence in the second code block both if statements will be exceuted to be false and nothing will be done with the response received.
I think you should go with the first approach because
Whether you are using raw JS or a library to implement this functionality, you'll have access to the state of the request i.e. whether the request was successful; met with an error and finally whether it has been completed.
Make proper use of these events and their respective callbacks to manipulate the UI for a better user experience. For example, if the request was unsuccessful, you'd want to update the user interface to reflect that their changes weren't successful while if it was successful, you'd want to tell them so. Don't keep the user waiting!
With jQuery, you'd make use of the success and error callbacks. You also get other callbacks such as complete and beforeSend to be invoked for apporopriate use.
$.ajax({
//Other code
success: function(msg)
{
// Update the UI here to reflect that the request was successful.
doSomethingClever();
},
error: function(msg)
{
// Update the UI here to reflect that the request was unsuccessful
doSomethingMoreClever();
},
complete: function(msg)
{
// Update the UI here to reflect completion
doSomethingEvenMoreClever();
}
});
take a look at this
I have a problem here, here is the code.
function addProduct () {
var prod_form = Ext.getCmp('productForm');
if(!prod_form.getForm().isValid()) {
Ext.Msg.show({
title:'Warning',
msg: 'Please verify the field(s) marked in <font color="red">red</font>',
icon: Ext.Msg.WARNING,
buttons: Ext.Msg.OK
})
return;
}
prod_form.getForm().submit({
url: 'save',
success: function(prod_form,action) {
console.log('SUCCESS');
Ext.Msg.show({
title: 'Adding the Product Successful',
msg: 'Data has been saved!'
})
},
failure : function(prod_form,action) {
console.log('FAILURE');
Ext.Msg.show({
title: 'Error',
msg: 'Failure in adding the Product',
buttons: Ext.Msg.OK
})
}
})
}
i have a function add product in extjs and it handles the button Add Product whenever it is clicked. then it will load the modal which has the form inside.
it do work but the problem is in the success and failure function.
It returns the failure function, but it does write the data in the database.
Why is this?
thanks!!
Make sure your back end returns success: true in the response payload otherwise all calls to form.submit() will "fail" even though it may have worked.
If you can't change the back end to return the success variable, you could use errorReader property of the form.
So for symfony 2 users, what you need to do is to create a json response,
success: true
with extjs,i m runnig the server from one system,giving the url in another system and i m using JSONP in extjs ver:4.02,when i check in response i m getting data in json format,when i try to print in console or Store im not getting..here is my extjs code...
<script type="text/javascript">
Ext.Ajax.cors = true;
Ext.Ajax.useDefaultXhrHeader = false;
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['empid', 'name', 'email']
});
var myStore = Ext.create('Ext.data.Store', {
model: 'User',
autoLoad:true,
proxy: {
type: 'jsonp',
// url : 'data/tagfamily.json',
url:'http://192.168.7.70:8080/palamanagement/user/getAllRecords',
},
listeners:{
'load':function( store, records, successful, eOpts ){
alert(records);
console.log(records);
}
}
});
You literally have to return something like the following:
someCallback({
users: [
{
id: 1,
name: "Ed Spencer",
email: "ed#sencha.com"
}
]
});
So you already have the JSON the way you need it; you just need your server response to wrap the JSON in the callback so that the JSONP proxy can execute and load your store with data.
Therefore, when handling the JSONP request, your server's script needs to recognize the "callback" param that is sent as a part of the request. It then needs to use the value of this param to wrap your JSON response.
Be sure to read the docs that both myself and Oguz have posted: they outline the requirement pretty well. But if you don't respond with a callback, you'll never get your standard JSON response to work with the JSONP proxy.
When you request from DOMAIN-A to DOMAIN-B, you should provide a call back function in proxy definition. The callback function will use in after request complete.
For instance, in Flickr REST service, there is jsoncallback parameter which we should give our function name in order to complete our request. In this way, our request url will be:
.../?jsoncallback=ourFunction
To be able to provide our function name, there is a property in ExtJS which is callbackKey.
Like so:
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['empid', 'name', 'email']
});
Ext.data.JsonP.request('http://api.flickr.com/services/feeds/photos_public.gne', {
callbackKey: 'jsoncallback',
callback: ourFunctionName
});
or
Ext.data.JsonP.request('http://api.flickr.com/services/feeds/photos_public.gne', {
callbackKey: 'jsoncallback',
callback: function(data) {
...
}
});
PS: I know, you will not find callback property in doc but just to be sure there is. Check ext-all-debug-w-comments.js file, line 108486
Flickr Callback Function
JsonP callbackKey
I have a store with a proxy configured to update my database.
proxy: {
type: "ajax",
api: {
create: MySite.app.BaseURL + 'Member.php?action=create',
read: MySite.app.BaseURL + 'Member.php',
update: MySite.app.BaseURL + 'Member.php?action=update',
destroy: MySite.app.BaseURL + 'Member.php?action=delete'
},
This all works fine but what I would really like is to be able to read the response so to report to the user success or failure of an update.
For example when an update is successful the json below is returned in the response,
{"success":true,"message":"Updated"}
And if not successful then the following is returned,
{"success":false,"message":"something terrible happened"}
I've tried adding a listener to the store as below but this doesn't seem to pick up the response.
listeners: {
success: function(response) {
console.log(response);
var data = Ext.JSON.decode(response.responseText.trim());
console.log(data);
if(data.success == 'true') {
console.log('success');
}
}
},
Could anyone help?
Stores don't fire a success event. That has to be configure in each operation using the success, failure or callback functions.
For example, when you perform a sync, you could do something like this:
myStore.sync({
success: function(batch, options) {
console.log(response);
}
});
Given stores work with bacths, to have to see the batch fields in order to know if it was okay or not.
The store will fire a 'write' event when successful. The proxy will fire an 'exception' event if there is a failure. This 'exception' event should bubble up to its parent (the store) so your listeners in the store should be for 'write' and 'exception'. You can look up the parameters to these events. For the 'write' event, the parameters should be the store itself, and the operation. The operation should have all the info you need to do the logging or whatever you want.
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.');
},
});
}
}