1. #1
    Sencha User Ronaldo's Avatar
    Join Date
    Jul 2007
    Location
    Enschede, The Netherlands
    Posts
    290
    Vote Rating
    0
    Ronaldo is on a distinguished road

      0  

    Default [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:

    Code:
    {
      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
      }]
    }
    Note that it's possible to
    - 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).



    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);
        }
    };
    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;
    }
    Attached Images
    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

  2. #2
    Sencha Premium Member
    Join Date
    Jun 2008
    Posts
    322
    Vote Rating
    4
    Scorpie is on a distinguished road

      0  

    Default


    Nice to see fellow Dutchies developing extensions
    I`m from Holland!

  3. #3
    Sencha User Ronaldo's Avatar
    Join Date
    Jul 2007
    Location
    Enschede, The Netherlands
    Posts
    290
    Vote Rating
    0
    Ronaldo is on a distinguished road

      0  

    Default


    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

  4. #4
    Sencha Premium Member
    Join Date
    Jun 2008
    Posts
    322
    Vote Rating
    4
    Scorpie is on a distinguished road

      0  

    Thumbs up


    Quote Originally Posted by Ronaldo View Post
    Heja, we should organize a Dutch Ext Meeting after the holiday
    Count me in!
    I`m from Holland!

  5. #5
    Sencha User Ronaldo's Avatar
    Join Date
    Jul 2007
    Location
    Enschede, The Netherlands
    Posts
    290
    Vote Rating
    0
    Ronaldo is on a distinguished road

      0  

    Default 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

  6. #6
    Sencha User dotnetwise's Avatar
    Join Date
    Mar 2010
    Location
    Iasi, Romania
    Posts
    31
    Vote Rating
    1
    dotnetwise is on a distinguished road

      0  

    Default


    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!

Thread Participants: 2

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi