[CLOSED] GridPanel RowExpander issues

  1. #1

    [CLOSED] GridPanel RowExpander issues

    I know there are issues with using the RowExpander and loading inner grids. But I'm not sure how else to handle my requirements (listed below). So I'm hoping to get some help either resolving the issues, or using a different solution that still meets requirements.

    Requirements:
    • Notify user on main grid row that there could be an issue with a document (loaded in RowExpander grid)
    • Allow user to select rows on main grid to be submitted to an action
    • When user expands a main row, they will see, and be able to click on associated records (Documents) to perform an action
    • When user expands a main row with an alert (Incomplete), they will see the document at issue and mouse over the alert icon to see a list of empty tables in a callout


    The issues I'm finding:
    • The main rows have rendered columns that substitute in icons for text (Preferred, Incomplete). When user expands any row, the icons move out of alignment - significantly.
    • When user expands a row, a grey bar shows between the main row content and the inner grid data.
    • When user expands the row with an incomplete alert, the callout works and user can mouse over entire area without issue. However, if a row without an alert is expanded, if user mouses vertically across the span of the row - javascript error(s) occur (screenshot).




    Here is the sample code:

    GridExpander.cshtml
    @model [YOUR PATH].Models.DebugViewModel
    
    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>GridExpander</title>
    
        <script type="text/javascript">
            var bulletStarUrl = Ext.net.ResourceMgr.getIconUrl("BulletStar");
            var bulletErrorUrl = Ext.net.ResourceMgr.getIconUrl("BulletError");
    
            var incompleteColumnRenderer = function (value, meta, record, index) {
                if (value == true)
                    return Ext.String.format("<img src='{0}' />", bulletErrorUrl);
    
                return "";
            };
    
            var preferredColumnRenderer = function (value, meta, record, index) {
                if (value == true)
                    return Ext.String.format("<img src='{0}' />", bulletStarUrl);
    
                return "";
            };
    
            var viewColumnPreparer = function (grid, command, record, row) {
                command.text = record.data.FileName;
            }
    
            var loadDocument = function (column, command, record, recordIndex, cellIndex) {
                alert("You would be loading: " + record.data.FileName);
            }
    
            var calloutOuterHtml = "<ul style='margin-left:5px; margin-bottom:0px; margin-top:0px; padding-left:0px;'>{0}</ul>";
            var setCalloutContent = function (callout) {
                var grid = callout.calloutOwner,
                    record = grid.view.getRecord(callout.triggerElement.dom.parentNode),
                    emptyTables = record.data.EmptyTables;
    
                if (emptyTables.length == 0) return false;
    
                var html = Ext.String.format(calloutOuterHtml, Ext.String.format("<li style='margin-bottom:2px;'>{0}</li>", emptyTables.split(",").join("</li><li style='margin-bottom:2px;'>")));
    
                callout.setHtml(html);
            }
        </script>
    </head>
    <body>
        @Html.X().ResourceManager()
    
        @(Html.X().Viewport()
            .Layout(LayoutType.Anchor)
            .Padding(10)
            .Items(
                Html.X().GridPanel()
                        .ID("GridPanelPeople")
                        .Title("People")
                        .Frame(true)
                        .Border(true)
                        .AnchorHorizontal("100%")
                        .Margin(10)
                        .ForceFit(true)
                        .MinHeight(500)
                        .Store(
                            Html.X().Store()
                                .ID("StorePeople")
                                .PageSize(20)
                                .DataSource(Model.People)
                                .Model(
                                    Html.X().Model()
                                        .IDProperty("PersonId")
                                        .Fields("PersonId",
                                                "First",
                                                "Last",
                                                "Email",
                                                "IsPreferred",
                                                "HasEmptyTables")
                                )
                        )
                        .ColumnModel(
                            Html.X().Column().DataIndex("First").Text("First"),
                            Html.X().Column().DataIndex("Last").Text("Last"),
                            Html.X().Column().DataIndex("Email").Text("Email"),
                            Html.X().Column()
                                .DataIndex("IsPreferred")
                                .Text("Preferred")
                                .Align(Alignment.Center)
                                .Width(32)
                                .Renderer(new Renderer() { Fn = "preferredColumnRenderer" }),
                            Html.X().Column()
                                .DataIndex("HasEmptyTables")
                                .Text("Incomplete")
                                .ItemID("HasEmptyTables")
                                .Align(Alignment.Center)
                                .Width(32)
                                .Renderer(new Renderer() { Fn = "incompleteColumnRenderer" })
                        )
                        .SelectionModel(
                            Html.X().RowSelectionModel()
                                .ID("RowSelectionModelDocument")
                                .Mode(SelectionMode.Simple)
                        )
                        .View(
                            Html.X().GridView().TrackOver(false)
                        )
                        .Plugins(
                            Html.X().RowExpander()
                                .ScrollOffset(10)
                                .Loader(
                                    Html.X().ComponentLoader()
                                        .Url(Url.Action("LoadDocuments"))
                                        .Mode(LoadMode.Component)
                                        .LoadMask(mask => mask.ShowMask = true)
                                        .Params(new { id = JRawValue.From("this.record.getId()") })
                                )
                        )
                        .BottomBar(
                            Html.X().PagingToolbar()
                        )
            )
    )
    </body>
    </html>
    DocumentGrid.cshtml
    @model [YOUR PATH].Models.DebugViewModel
    
    @(Html.X().GridPanel()
        .Border(false)
        .Header(false)
        .HideHeaders(true)
        .EnableColumnHide(false)
        .DisableSelection(true)
        .Store(
            Html.X().Store()
                .DataSource(Model.Documents)
                .Model(
                    Html.X().Model()
                        .Fields(
                            Html.X().ModelField().Name("DocumentId").Type(ModelFieldType.Int),
                            Html.X().ModelField().Name("FileName"),
                            Html.X().ModelField().Name("HasEmptyTables").Type(ModelFieldType.Boolean),
                            Html.X().ModelField().Name("EmptyTables")
                        )
                )
        )
        .ColumnModel(
            Html.X().Column()
                .Width(420)
                .RightCommandAlign(false)
                .MenuDisabled(true)
                .Hideable(false)
                .PrepareCommand(p => p.Fn = "viewColumnPreparer")
                .Commands(
                    Html.X().ImageCommand()
                        .CommandName("ViewReport")
                        .Icon(Icon.PageWhiteAcrobat)
                )
                .Listeners(l =>
                {
                    l.Command.Fn = "loadDocument";
                }),
            Html.X().Column()
                .DataIndex("HasEmptyTables")
                .ItemID("HasEmptyTables")
                .Align(Alignment.Center)
                .Width(32)
                .Renderer(new Renderer() { Fn = "incompleteColumnRenderer" })
        )
        .View(
            Html.X().GridView().TrackOver(false)
        )
        .Callouts(cos =>
        {
            cos.Add(
                Html.X().Callout()
                    .Title("Empty Tables")
                    .HeaderStyle("font-size:9pt;")
                    .Trigger(CalloutTrigger.Hover)
                    .Alignment(AnchorPoint.Left)
                    .Delegate(".x-grid-cell-HasEmptyTables")
                    .UI(UI.Warning)
                    .Listeners(l =>
                    {
                        l.BeforeShow.Fn = "setCalloutContent";
                    })
            );
        })
    )
    Controller
    [AllowAnonymous]
    public ActionResult GridExpander()
    {
        return View(new DebugViewModel()
        {
            People = new List<DebugViewModel.Person>()
            {
                new DebugViewModel.Person { PersonId=1, First="Homer", Last="Simpson", Email="hsimpson@springfield.net", IsPreferred=true, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=2, First="Montgomery", Last="Burns", Email="hburns@springfield.net", IsPreferred=true, HasEmptyTables=true },
                new DebugViewModel.Person { PersonId=3, First="Krusty", Last="Clown", Email="kclown@springfield.net", IsPreferred=false, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=4, First="Edna", Last="Krabappel", Email="ekrabappel@springfield.net", IsPreferred=false, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=5, First="Bumblebee", Last="Man", Email="bman@springfield.net", IsPreferred=false, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=6, First="Selma", Last="Bouvier", Email="sbouvier@springfield.net", IsPreferred=true, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=7, First="Gary", Last="Chalmers", Email="gchalmers@springfield.net", IsPreferred=true, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=8, First="Apu", Last="Nahasapeemapetilon", Email="anahasapeemapetilon@springfield.net", IsPreferred=true, HasEmptyTables=false },
                new DebugViewModel.Person { PersonId=9, First="Ned", Last="Flanders", Email="nflanders@springfield.net", IsPreferred=true, HasEmptyTables=false },
            }
        });
    }
    
    public ActionResult LoadDocuments(int id)
    {
        int id1 = id *10;
        int id2 = id1 + 1;
        bool hasEmptyTables = false;
        string emptyTables = String.Empty;
        if (id == 2)
        {
            hasEmptyTables = true;
            emptyTables = "Table One,Table Two,Table Three";
        }
        
        return this.ComponentConfig("DocumentGrid",
            new DebugViewModel()
            {
                Documents = new List<DebugViewModel.Document>()
                {
                    new DebugViewModel.Document { DocumentId=id1, FileName="Document " + id1 + ".pdf", EmptyTables=emptyTables, HasEmptyTables=hasEmptyTables },
                    new DebugViewModel.Document { DocumentId=id2, FileName="Document " + id2 + ".pdf", EmptyTables=String.Empty, HasEmptyTables=false }
                }
            }
        );
    }
    Model
    using System.Collections.Generic;
    
    namespace [YOUR PATH].Models
    {
        public class DebugViewModel
        {
            public List<Person> People { get; set; }
            public List<Document> Documents { get; set; }
    
            public class Person
            {
                public int PersonId { get; set; }
                public string First { get; set; }
                public string Last { get; set; }
                public string Email { get; set; }
                public bool IsPreferred { get; set; }
                public bool HasEmptyTables { get; set; }
            }
    
            public class Document
            {
                public int DocumentId { get; set; }
                public string FileName { get; set; }
                public bool HasEmptyTables { get; set; }
                public string EmptyTables { get; set; }
            }
        }
    }
    Again ... I'm open to alternate suggestions.

    Thanks! Joel
    Attached Thumbnails Click image for larger version. 

Name:	grid-expand-jserror.png 
Views:	177 
Size:	46.6 KB 
ID:	10341  
    Last edited by Daniil; May 16, 2014 at 2:38 PM. Reason: [CLOSED]
  2. #2
    Hi @jpadgett,

    Quite a big post:) It will take some time to respond.
  3. #3
    Quote Originally Posted by jpadgett View Post
    • The main rows have rendered columns that substitute in icons for text (Preferred, Incomplete). When user expands any row, the icons move out of alignment - significantly.
    That is strange issue.

    Please try to replace
    .ItemID("HasEmptyTables")
    in the DocumentGrid.cshtml with
    .ItemID("HasEmptyTablesInner")
    Respectively, you should change the Callout's Delegate to
    .Delegate(".x-grid-cell-HasEmptyTablesInner")
    Quote Originally Posted by jpadgett View Post
    • When user expands a row, a grey bar shows between the main row content and the inner grid data.
    You can cut it off by setting up
    .Cls("my-inner-grid")
    for the inner GridPanel.

    The CSS class definition is:
    <style>
        .my-inner-grid .x-grid-body {
            border-top: none;
        }
    </style>
    Quote Originally Posted by jpadgett View Post
    • When user expands the row with an incomplete alert, the callout works and user can mouse over entire area without issue. However, if a row without an alert is expanded, if user mouses vertically across the span of the row - javascript error(s) occur (screenshot).
    I cannot reproduce. It might be fixed in v2.5.1. Please clarify what version are you working with?
  4. #4
    Daniil - thanks for the suggestions. Issues 1 and 2 are resolved.

    I am using version 2.5.1 - runtime version of Ext.NET lib is v4.0.30319

    My environment is Windows 7 64-bit, VS 2012, using VS's IIS Express for development testing.

    To reproduce the javascript error, I usually expand two or three rows, then run my mouse cursor from the middle-top to the middle-bottom of the grid and back a couple of times - making sure it is going over the expanded rows. No mouse clicks.

    Let me know if the additional info helps in reproducing in your testing.

    Thanks!
    Joel
  5. #5
    I still cannot reproduce. What is the browser? Is it reproducible in any browser or in a specific one only?

    Also a screencast could clarify the steps to reproduce.

    That "isCollapsePlacehodler" error, could you post the JavaScript code where it occurs?

    Could you also test with Ext.NET from the SVN trunk?
  6. #6
    Quote Originally Posted by Daniil View Post
    I still cannot reproduce. What is the browser?
    Internet Exporer 11 - Version 11.0.9600.17105

    Quote Originally Posted by Daniil View Post
    Is it reproducible in any browser or in a specific one only?
    Tested with Chrome, Version 34.0.1847.131 m - the error did not occur
    Tested with Firefox, Version 29 - the error did not occur

    Quote Originally Posted by Daniil View Post
    That "isCollapsePlacehodler" error, could you post the JavaScript code where it occurs?
    Ext.view.Table.override({onUpdate:Ext.Function.createInterceptor(Ext.view.Table.prototype.onUpdate,function(store,record,operation,changedFieldNames){var me=this,index;if(store.indexOf){index=store.indexOf(record);}
    else if(record.parentNode){index=record.parentNode.indexOf(record);}
    me.fireEvent('beforeitemupdate',me,record,index);}),processUIEvent:function(e){if(this.stopEventFn&&this.stopEventFn(this,e)===false){return false;}
    return this.callParent(arguments);},moveColumn:function(fromIdx,toIdx,colsToMove){var me=this,fragment=(colsToMove>1)?document.createDocumentFragment():undefined,destinationCellIdx=toIdx,colCount=me.getGridColumns().length,lastIndex=colCount-1,doFirstLastClasses=(me.firstCls||me.lastCls)&&(toIdx===0||toIdx==colCount||fromIdx===0||fromIdx==lastIndex),i,j,rows,len,tr,cells,tables;if(me.rendered&&toIdx!==fromIdx){rows=me.el.query(me.getDataRowSelector());if(me.panel&&me.panel.getRowExpander&&me.panel.getRowExpander()){rows=Ext.Array.filter(rows,function(item){return!Ext.fly(item).findParent("div.x-grid-rowbody",me.el);});}
    if(toIdx>fromIdx&&fragment){destinationCellIdx-=colsToMove;}
    for(i=0,len=rows.length;i<len;i++){tr=rows[i];cells=tr.childNodes;if(doFirstLastClasses){if(cells.length===1){Ext.fly(cells[0]).addCls(me.firstCls);Ext.fly(cells[0]).addCls(me.lastCls);continue;}
    if(fromIdx===0){Ext.fly(cells[0]).removeCls(me.firstCls);Ext.fly(cells[1]).addCls(me.firstCls);}else if(fromIdx===lastIndex){Ext.fly(cells[lastIndex]).removeCls(me.lastCls);Ext.fly(cells[lastIndex-1]).addCls(me.lastCls);}
    if(toIdx===0){Ext.fly(cells[0]).removeCls(me.firstCls);Ext.fly(cells[fromIdx]).addCls(me.firstCls);}else if(toIdx===colCount){Ext.fly(cells[lastIndex]).removeCls(me.lastCls);Ext.fly(cells[fromIdx]).addCls(me.lastCls);}}
    if(fragment){for(j=0;j<colsToMove;j++){fragment.appendChild(cells[fromIdx]);}
    tr.insertBefore(fragment,cells[destinationCellIdx]||null);}else{tr.insertBefore(cells[fromIdx],cells[destinationCellIdx]||null);}}
    tables=me.el.query(me.getBodySelector());for(i=0,len=tables.length;i<len;i++){tr=tables[i];if(fragment){for(j=0;j<colsToMove;j++){fragment.appendChild(tr.childNodes[fromIdx]);}
    tr.insertBefore(fragment,tr.childNodes[destinationCellIdx]||null);}else{tr.insertBefore(tr.childNodes[fromIdx],tr.childNodes[destinationCellIdx]||null);}}}},getFeature:function(id){var f=this.callParent(arguments);if(!f){var features=this.featuresMC;if(features){return features.getAt(features.findIndex("proxyId",id));}}
    return f;},hasActiveGrouping:function(){return this.isGrouping&&this.store.isGrouped();},getRecord:function(node){var me=this,record,recordIndex;if(me.store.isDestroyed){return;}
    node=me.getNode(node);if(node){if(!me.hasActiveGrouping()){recordIndex=node.getAttribute('data-recordIndex');if(recordIndex){recordIndex=parseInt(recordIndex,10);if(recordIndex>-1){return me.store.data.getAt(recordIndex);}}}
    record=me.store.getByInternalId(node.getAttribute('data-recordId'));if(!record){record=this.dataSource.data.get(node.getAttribute('data-recordId'));}
    return record;}},
    
    
    indexInStore:function(node){node=node.isCollapsedPlaceholder?this.getNode(node):this.getNode(node,false);
    
    
    if(!node&&node!==0){return-1;}
    var recordIndex=node.getAttribute('data-recordIndex');if(recordIndex){return parseInt(recordIndex,10);}
    return this.dataSource.indexOf(this.getRecord(node));},getRowStyleTableElOriginal:Ext.view.Table.prototype.getRowStyleTableEl,getRowStyleTableEl:function(){var el=this.getRowStyleTableElOriginal.apply(this,arguments);if(!el){el={addCls:Ext.emptyFn,removeCls:Ext.emptyFn,tagName:{}}}
    return el;},renderRow:function(record,rowIdx,out){var me=this,isMetadataRecord=rowIdx===-1,selModel=me.selModel,rowValues=me.rowValues,itemClasses=rowValues.itemClasses,rowClasses=rowValues.rowClasses,cls,rowTpl=me.rowTpl;rowValues.record=record;rowValues.recordId=record.internalId;rowValues.recordIndex=rowIdx;rowValues.rowId=me.getRowId(record);rowValues.itemCls=rowValues.rowCls='';if(!rowValues.columns){rowValues.columns=me.ownerCt.columnManager.getColumns();}
    itemClasses.length=rowClasses.length=0;if(!isMetadataRecord){itemClasses[0]=Ext.baseCSSPrefix+"grid-row";if(selModel&&selModel.isRowSelected){if((rowIdx+1)<selModel.store.getTotalCount()){if(selModel.isRowSelected(rowIdx+1)){itemClasses.push(me.beforeSelectedItemCls);}}
    if(selModel.isRowSelected(record)){itemClasses.push(me.selectedItemCls);}}
    if(me.stripeRows&&rowIdx%2!==0){rowClasses.push(me.altRowCls);}
    if(me.getRowClass){cls=me.getRowClass(record,rowIdx,null,me.dataSource);if(cls){rowClasses.push(cls);}}}
    if(out){rowTpl.applyOut(rowValues,out);}else{return rowTpl.apply(rowValues);}}});

    Quote Originally Posted by Daniil View Post
    Could you also test with Ext.NET from the SVN trunk?
    I will do this later today.


    Thanks again for all of your help.
    Joel
  7. #7
    This is the old code:
    node=node.isCollapsedPlaceholder?this.getNode(node):this.getNode(node,false);
    The new one in v2.5.1 is
    node = (node && node.isCollapsedPlaceholder) ? this.getNode(node) : this.getNode(node, false);
    Somehow it loads the old script for you. Please double ensure v2.5.1 dlls are applied and try to clear a browser's cache.
  8. #8
    I updated Ext.NET using nuget, and the path to the Ext.Net.dll file is ...\Source\packages\Ext.NET.MVC.2.5.1\lib\net45

    However, when I look at the details of the .dll, the file version is 2.5.0.31282

    Should I download the MVC package from the website instead of nuget?
  9. #9
    Well - I downloaded from the website and replaced the .dll. The javascript error no longer occurs.

    But shouldn't the nuget package for 2.5.1 install the 2.5.1 dll instead of the 2.5.0 one?
  10. #10
    It must.

    I just tried
    Install-Package Ext.NET
    It installs v2.5.1 Ex.Net.dll for me. Hard to say why it doesn't do the same for you.

Similar Threads

  1. Replies: 15
    Last Post: Sep 19, 2017, 6:15 PM
  2. [CLOSED] Layout issue: Show Gridpanel in gridpanel with rowexpander
    By rmelancon in forum 2.x Legacy Premium Help
    Replies: 15
    Last Post: Mar 05, 2014, 3:01 AM
  3. [CLOSED] Issue with Show Gridpanel in gridpanel with rowexpander
    By rmelancon in forum 2.x Legacy Premium Help
    Replies: 2
    Last Post: Feb 26, 2014, 5:19 PM
  4. Replies: 1
    Last Post: Jun 15, 2011, 9:01 AM
  5. [CLOSED] bind gridpanel in rowexpander of gridpanel
    By idrissb in forum 1.x Legacy Premium Help
    Replies: 2
    Last Post: Sep 03, 2009, 1:54 PM

Tags for this Thread

Posting Permissions