-
23 Jun 2009 10:52 AM #1
[3.0RC2] GridBatchErrorDisplayPlugin alfa
[3.0RC2] GridBatchErrorDisplayPlugin alfa
Hi all,
As mentioned here, I'm working on the new batch save functionality, and more specifically, on visualizing the errors returned for unsuccessfully saved records.
AFAIK, Ext 3.0 assumes (wrongfully IMHO) that, when saving a batch, all records were saved succesfully (or all were rejected). With this plugin, you can return the following json:
Note that it's possible toCode:{ success: true, data: [{"username":"","id":"1"},{"email":"","id":"2"}], errors: [{ "id": 1, // Error for record with id=1, field 'username' "field": "username", "msg": "The field username can not be empty." }, { "id": 2, "field": "email", // Error for record with id=2, field 'email' "msg": "The field email can not be empty." }, { "id": 2, "field": "email", // Another error for record with id=2, field 'email' "msg": "Invalid email address." // Must conform to x@y.z }] }
- return multiple errors for the same record / different fields
- return multiple errors for the same record / same field (as in email, record id=2)
Visually, this amounts to the pictures attached. The tooltip shows all the server side generated error messages for that record/field.
I'm using the private dataView.findCellIndex method, I don't know why it is private, as there's also a findRowIndex method. Anyway, it works for now.
Note that the plugin is not production ready, I'd like to hear your opinion, and if you have any code improvements (there should be many), please let me know.
For one thing, I'm using the innerHTML dom property, but it should be easy to insert a div with a certain class using Ext, shouldn't it? (See the onViewUpdate method).
Here's the css you need to include (You may need to adapt the path):Code:/** * GridBatchErrorDisplayPlugin v0.1 - Display error messages for batch save * functionality in Ext 3.0 RC2. * See http://extjs.com/forum/newthread.php?do=newthread&f=42 * * Provided without as is and without warranty. You may use this plugin anywhere you * want, but you cannot sell it. */ Ext.twensoc.GridBatchErrorDisplayPlugin = function(config) { config = config || {}; Ext.apply(this, config); } Ext.twensoc.GridBatchErrorDisplayPlugin.prototype = { init : function(grid){ this.errors = []; this.grid = grid; grid.store.on('write', this.onWrite, this); grid.store.on('update', this.onUpdate, this); grid.view.on('refresh', this.onViewUpdate, this); grid.view.on('rowupdated', this.onViewUpdate, this); grid.on('render', this.onGridRender, this); }, onGridRender: function(grid) { grid.tip = new Ext.ToolTip({ target: grid.view.mainBody, // The overall target element. delegate: '.x-grid3-cell-error', // Each grid row causes its own seperate show and hide. renderTo: document.body, // Render immediately so that tip.body can be referenced prior to the first show. listeners: { // Change content dynamically depending on which element triggered the show. scope: this, beforeshow: function updateTipBody(tip) { var v = this.grid.view; var r = this.grid.store.getAt(v.findRowIndex(tip.triggerElement)); var fld = this.grid.colModel.config[v.findCellIndex(tip.triggerElement)].name; var errs = r.errs.get(fld); if(!errs) { return; } var msg = ""; Ext.each(errs, function(m) { msg += m + "<br/>"; }, this); tip.body.dom.innerHTML = msg; } } }) }, onViewUpdate: function(view, firstRow, r) { if(!r || !r.errs) { return; } var rowIndex = this.grid.store.data.indexOf(r); r.errs.eachKey(function(k, v) { var cell = view.getCell(rowIndex, this.grid.colModel.findColumnIndex(k)); Ext.fly(cell).addClass("x-grid3-cell-error"); }, this); }, onUpdate:function(s, r, operation) { if(operation != Ext.data.Record.COMMIT || this.errors.length==0) { return; } var errs = []; Ext.each(this.errors, function(e) { if(e.id == r.id) { errs.push(e); } }, this); if(errs.length==0) { return; // No errors found for record r } // One or more errors found for record r, remove the all from the errors list received Ext.each(errs, function(e) { this.errors.remove(e); }, this); var v = this.grid.view; var se = this.grid.store.errors; if(r.errs) { r.errs.clear(); } else { r.errs = new Ext.util.MixedCollection(); } Ext.each(errs, function(e) { r.dirty = true; if(!r.modified){ r.modified = {}; } r.modified[e.field] = r.data[e.field]; // 'Remodify' other fields from the record // that produced a server side error when saved var m = e.modified; for(var p in m){ r.modified[p] = m[p]; } // Register the record as changed in the store... s.afterEdit(r); r.errs = r.errs || new Ext.util.MixedCollection(); // Save errormsg per field in an array in the record.errs (mixedCollection) // To allow multiple errors per field, save all messages in an array var msgs = r.errs.get(e.field); if(!msgs) { msgs = []; r.errs.add(e.field, msgs); } msgs.push(e.msg); }, this); }, onWrite: function(s, action, result, res, rs) { // Clear errors from a previous batch request this.errors = []; Ext.each(res.errors, function(e) { // Save the error in the plugin, to be retrieved in the onUpdate listener. this.errors.push(e); var r=s.getById(e.id); // Clone the modified fields of the record e.modified = r.getChanges(); }, this); } };
Code:.x-grid3-cell-error { background-image:url(/js/ext3.0/resources/images/default/grid/invalid_line.gif); background-position:0 16px; background-repeat:repeat-x; width:198px; }Last edited by Ronaldo; 23 Jun 2009 at 2:02 PM. Reason: updated error display
Ronald van Raaphorst aka Ronaldo
I'm a freelance software developer in Java, PHP, and ExtJs.
Skyperonald_twensoc
Mailinfo@twensoc.nl
-
25 Jun 2009 12:32 AM #2
Nice to see fellow Dutchies developing extensions
I`m from Holland!
-
27 Jun 2009 11:16 AM #3
Heja, we should organize a Dutch Ext Meeting after the holiday
Ronald van Raaphorst aka Ronaldo
I'm a freelance software developer in Java, PHP, and ExtJs.
Skyperonald_twensoc
Mailinfo@twensoc.nl
-
29 Jun 2009 1:48 AM #4
-
20 Jul 2009 4:46 AM #5
Updated for 3.0
Updated for 3.0
Hi all,
Here's a new version that's updated for the latest 3.0 release. Don't forget to include this css:
Here's the css you need to include (You may need to adapt the path):
Code:.x-grid3-cell-error { background-image:url(/js/ext3.0/resources/images/default/grid/invalid_line.gif); background-position:0 16px; background-repeat:repeat-x; width:198px; }Code:/** * GridBatchErrorDisplayPlugin v0.2 - Display error messages for batch save * functionality in Ext 3.0. * See http://extjs.com/forum/newthread.php?do=newthread&f=42 * * Provided without as is and without warranty. You may use this plugin anywhere you * want, but you cannot sell it. */ Ext.twensoc.GridBatchErrorDisplayPlugin = function(config) { config = config || {}; Ext.apply(this, config); } Ext.twensoc.GridBatchErrorDisplayPlugin.prototype = { init : function(grid){ this.errors = []; this.grid = grid; grid.store.proxy.on('write', this.onWrite, this); grid.store.on('update', this.onUpdate, this); grid.view.on('refresh', this.onViewUpdate, this); grid.view.on('rowupdated', this.onViewUpdate, this); grid.on('render', this.onGridRender, this); }, onGridRender: function(grid) { grid.tip = new Ext.ToolTip({ target: grid.view.mainBody, // The overall target element. delegate: '.x-grid3-cell-error', // Each grid row causes its own seperate show and hide. renderTo: document.body, // Render immediately so that tip.body can be referenced prior to the first show. listeners: { // Change content dynamically depending on which element triggered the show. scope: this, beforeshow: function updateTipBody(tip) { var v = this.grid.view; var r = this.grid.store.getAt(v.findRowIndex(tip.triggerElement)); var fld = this.grid.colModel.config[v.findCellIndex(tip.triggerElement)].name; var errs = r.errs.get(fld); if(!errs) { return; } var msg = ""; Ext.each(errs, function(m) { msg += m + "<br/>"; }, this); tip.body.dom.innerHTML = msg; } } }) }, onViewUpdate: function(view, firstRow, r) { if(!r || !r.errs) { return; } var rowIndex = this.grid.store.data.indexOf(r); r.errs.eachKey(function(k, v) { var cell = view.getCell(rowIndex, this.grid.colModel.findColumnIndex(k)); Ext.fly(cell).addClass("x-grid3-cell-error"); }, this); }, onUpdate:function(s, r, operation) { if(operation != Ext.data.Record.COMMIT || this.errors.length==0) { return; } var errs = []; Ext.each(this.errors, function(e) { if(e.id == r.id) { errs.push(e); } }, this); if(errs.length==0) { return; // No errors found for record r } // One or more errors found for record r, remove the all from the errors list received Ext.each(errs, function(e) { this.errors.remove(e); }, this); var v = this.grid.view; var se = this.grid.store.errors; if(r.errs) { r.errs.clear(); } else { r.errs = new Ext.util.MixedCollection(); } Ext.each(errs, function(e) { r.dirty = true; if(!r.modified){ r.modified = {}; } r.modified[e.field] = r.data[e.field]; // 'Remodify' other fields from the record // that produced a server side error when saved var m = e.modified; for(var p in m){ r.modified[p] = m[p]; } // Register the record as changed in the store... s.afterEdit(r); r.errs = r.errs || new Ext.util.MixedCollection(); // Save errormsg per field in an array in the record.errs (mixedCollection) // To allow multiple errors per field, save all messages in an array var msgs = r.errs.get(e.field); if(!msgs) { msgs = []; r.errs.add(e.field, msgs); } msgs.push(e.msg); }, this); }, /** * Callback from the proxy. We need to extract errors from the result and save them * locally (to this plugin) before the grid is updated and rendered, as this is the only change * we have to get the complete json result */ onWrite: function(proxy, action, data, response, rs, arg) { var s=this.grid.store; // Clear errors from a previous batch request this.errors = []; s.data.each(function(item) { delete(item.errs); }); // Copy errors locally Ext.each(response.errors, function(e) { this.errors.push(e); var r=s.getById(e.id); // Clone the modified fields of the record e.modified = r.getChanges(); }, this); } };Ronald van Raaphorst aka Ronaldo
I'm a freelance software developer in Java, PHP, and ExtJs.
Skyperonald_twensoc
Mailinfo@twensoc.nl
-
30 Jan 2011 8:49 AM #6
Very nice plugin!
I can see that it only works for 'update' action.
Thus "phantom" records are not working the same way.
I have made some changes and now everything works like a charm on 3.3.1.
I hope I will have time to share the code soon!


Reply With Quote