PDA

View Full Version : [CLOSED] GridPanel RowExpander issues



jpadgett
May 01, 2014, 11:28 PM
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).


http://forums.ext.net/attachment.php?attachmentid=10341&stc=1

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.par entNode),
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

Daniil
May 02, 2014, 7:14 PM
Hi @jpadgett,

Quite a big post:) It will take some time to respond.

Daniil
May 06, 2014, 12:42 PM
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")




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>




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?

jpadgett
May 06, 2014, 6:34 PM
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

Daniil
May 07, 2014, 6:20 AM
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?

jpadgett
May 08, 2014, 2:51 PM
I still cannot reproduce. What is the browser?
Internet Exporer 11 - Version 11.0.9600.17105


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


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


Ext.view.Table.override({onUpdate:Ext.Function.cre ateInterceptor(Ext.view.Table.prototype.onUpdate,f unction(store,record,operation,changedFieldNames){ var me=this,index;if(store.indexOf){index=store.indexO f(record);}
else if(record.parentNode){index=record.parentNode.inde xOf(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(f romIdx,toIdx,colsToMove){var me=this,fragment=(colsToMove>1)?document.createDocumentFragment():undefined,des tinationCellIdx=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.ren dered&&toIdx!==fromIdx){rows=me.el.query(me.getDataRowSel ector());if(me.panel&&me.panel.getRowExpander&&me.panel.getRowExpander()){rows=Ext.Array.filter(r ows,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(cel ls.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,l en=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.hasActiveGro uping()){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.dat a.get(node.getAttribute('data-recordId'));}
return record;}},


indexInStore:function(node){node=node.isCollapsedP laceholder?this.getNode(node):this.getNode(node,fa lse);


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));},ge tRowStyleTableElOriginal:Ext.view.Table.prototype. getRowStyleTableEl,getRowStyleTableEl:function(){v ar el=this.getRowStyleTableElOriginal.apply(this,argu ments);if(!el){el={addCls:Ext.emptyFn,removeCls:Ex t.emptyFn,tagName:{}}}
return el;},renderRow:function(record,rowIdx,out){var me=this,isMetadataRecord=rowIdx===-1,selModel=me.selModel,rowValues=me.rowValues,item Classes=rowValues.itemClasses,rowClasses=rowValues .rowClasses,cls,rowTpl=me.rowTpl;rowValues.record= record;rowValues.recordId=record.internalId;rowVal ues.recordIndex=rowIdx;rowValues.rowId=me.getRowId (record);rowValues.itemCls=rowValues.rowCls='';if( !rowValues.columns){rowValues.columns=me.ownerCt.c olumnManager.getColumns();}
itemClasses.length=rowClasses.length=0;if(!isMetad ataRecord){itemClasses[0]=Ext.baseCSSPrefix+"grid-row";if(selModel&&selModel.isRowSelected){if((rowIdx+1)<selModel.store.getTotalCount()){if(selModel.isRowS elected(rowIdx+1)){itemClasses.push(me.beforeSelec tedItemCls);}}
if(selModel.isRowSelected(record)){itemClasses.pus h(me.selectedItemCls);}}
if(me.stripeRows&&rowIdx%2!==0){rowClasses.push(me.altRowCls);}
if(me.getRowClass){cls=me.getRowClass(record,rowId x,null,me.dataSource);if(cls){rowClasses.push(cls) ;}}}
if(out){rowTpl.applyOut(rowValues,out);}else{retur n rowTpl.apply(rowValues);}}});




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

Daniil
May 09, 2014, 1:13 PM
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.

jpadgett
May 12, 2014, 2:20 PM
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?

jpadgett
May 12, 2014, 2:32 PM
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?

Daniil
May 12, 2014, 3:47 PM
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.