问题
We are implementing a user preference to (instantly) show "more" or "less" data on the grid. "More" should increase the row-height (with every row having the same increased height).
When the user toggles, we update our DataView, and call setOptions on the grid with an updated rowHeight value. We then call invalidate() and render().
But row height isn't being updated. :(
Could someone advise a solution? Should we alter the height directly via CSS? If so, any tips on doing this?
回答1:
Indeed it is possible to dynamically update row height based on user interaction. the Slickgrid API
provides all that we need.
Because:
- we can add/remove rows dynamically;
- we can dynamically apply custom
css
at row & cell level.
Here is a simple demo to get things started:
////////////////////////////////////////////////////////////////////////////////
//example codez re trying to create a grid with rows of dynamic height to
//cater for folks that wanna bung loads of stuff in a field & see it all...
//by violet313@gmail.com ~ visit: www.violet313.org/slickgrids
//have all the fun with it ;) vxx.
////////////////////////////////////////////////////////////////////////////////
modSlickgridSimple=(
function()
{
var _dataView=null;
var _grid=null;
var _data=[];
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
var getPaddingItem=function(parent , offset)
{
var item={};
for (var prop in _data[0]) item[prop]=null;
item.id=parent.id+"."+offset;
//additional hidden padding metadata fields
item._collapsed= true;
item._isPadding= true;
return item;
}
//////////////////////////////////////////////////////////////
//this just builds our expand collapse button
//////////////////////////////////////////////////////////////
var onRenderIDCell=function(row, cell, value, columnDef, item)
{
if (item._isPadding==true); //render nothing
else if (item._collapsed) return "<div class='toggle expand'></div>";
else
{
var html=[];
var rowHeight=_grid.getOptions().rowHeight;
//V313HAX:
//putting in an extra closing div after the closing toggle div and ommiting a
//final closing div for the detail ctr div causes the slickgrid renderer to
//insert our detail div as a new column ;) ~since it wraps whatever we provide
//in a generic div column container. so our detail becomes a child directly of
//the row not the cell. nice =) ~no need to apply a css change to the parent
//slick-cell to escape the cell overflow clipping.
//sneaky extra </div> inserted here-----------------v
html.push("<div class='toggle collapse'></div></div>");
html.push("<div class='dynamic-cell-detail' "); //apply custom css to detail
html.push("style='height:", item._height, "px;"); //set total height of padding
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
html.push("<div>",item._detailContent,"</div>"); //sub ctr for custom styling
//&omit a final closing detail container </div> that would come next
return html.join("");
}
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
var onRowClick=function(e, args)
{
_dataView.beginUpdate();
if ($(e.target).hasClass("toggle"))
{
var item=_dataView.getItem(args.row);
if (item)
{
if (!item._collapsed)
{
item._collapsed=true;
for (var idx=1; idx<=item._sizePadding; idx++)
_dataView.deleteItem(item.id+"."+idx);
item._sizePadding=0;
}
else
{
item._collapsed=false;
kookupDynamicContent(item);
var idxParent=_dataView.getIdxById(item.id);
for (var idx=1; idx<=item._sizePadding; idx++)
_dataView.insertItem(idxParent+idx, getPaddingItem(item,idx));
}
_dataView.updateItem(item.id, item);
}
e.stopImmediatePropagation();
}
_dataView.endUpdate();
}
//////////////////////////////////////////////////////////////
var gridOptions={ enableColumnReorder: true };
//////////////////////////////////////////////////////////////
var _gridColumns=
[
{
id: "id",
name: "",
field: "id",
resizable: false,
width: 20,
formatter: onRenderIDCell,
},
{id: "title", name: "Title", field: "title", resizable: true},
{id: "duration", name: "Duration", field: "duration", resizable: true},
{id: "pcComplete", name: "% Complete", field: "pcComplete", resizable: true},
{id: "start", name: "Start", field: "start", resizable: true},
{id: "finish", name: "Finish", field: "finish", resizable: true},
{id: "effortDriven", name: "Effort Driven", field: "effortDriven", resizable: true},
];
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
var kookupTestData=(function()
{
for (var i = 0; i < 100; i++)
_data[i] =
{
id: i,
title: "Task " + i,
duration: "5 days",
pcComplete: Math.round(Math.random() * 100),
start: "01/01/2009",
finish: "01/05/2009",
effortDriven: (i % 5 == 0),
//additional hidden metadata fields
_collapsed: true,
_sizePadding: 0, //the required number of pading rows
_height: 0, //the actual height in pixels of the detail field
_isPadding: false,
};
})();
//////////////////////////////////////////////////////////////
//create the detail ctr node. this belongs to the dev & can be custom-styled as per
//////////////////////////////////////////////////////////////
var kookupDynamicContent=function(item)
{
//add some random oooks as fake detail content
var oookContent=[];
var oookCount=Math.round(Math.random() * 12)+1;
for (var next=0; next<oookCount; next++)
oookContent.push("<div><span>oook</span></div>");
item._detailContent=oookContent.join("");
//calculate padding requirements based on detail-content..
//ie. worst-case: create an invisible dom node now &find it's height.
var lineHeight=13; //we know cuz we wrote the custom css innit ;)
item._sizePadding=Math.ceil((oookCount*lineHeight) / _grid.getOptions().rowHeight);
item._height=(item._sizePadding * _grid.getOptions().rowHeight);
}
//////////////////////////////////////////////////////////////
//jquery onDocumentLoad
//////////////////////////////////////////////////////////////
$(function()
{
//initialise the data-model
_dataView=new Slick.Data.DataView();
_dataView.beginUpdate();
_dataView.setItems(_data);
_dataView.endUpdate();
//initialise the grid
_grid=new Slick.Grid("#grid-simple", _dataView, _gridColumns);
_grid.onClick.subscribe(onRowClick);
//wire up model events to drive the grid per DataView requirements
_dataView.onRowCountChanged.subscribe
(function(){ _grid.updateRowCount();_grid.render(); });
_dataView.onRowsChanged.subscribe
(function(e, a){ _grid.invalidateRows(a.rows);_grid.render(); });
$(window).resize(function() {_grid.resizeCanvas()});
});
}
)();
//////////////////////////////////////////////////////////////
//done ;)
::-webkit-scrollbar
{
width: 12px;
background-color: #B9BACC;
}
::-webkit-scrollbar-track
{
color: #fff;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb
{
color: #96A9BB;
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
}
body
{
font-family: Arial, Helvetica, sans-serif;
background-color: #131313;
position: absolute;
top: 5px;
bottom: 5px;
left: 5px;
right: 5px;
}
#grid-simple
{
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
margin: auto;
font-size: 12px;
background-color: #ECEEE9;
}
.toggle
{
height: 16px;
width: 16px;
display: inline-block;
}
.toggle.expand
{
background: url(http://tinyurl.com/k9ejb3a/expand.gif) no-repeat center center;
}
.toggle.collapse
{
background: url(http://tinyurl.com/k9ejb3a/collapse.gif) no-repeat center center;
}
/*--- generic slickgrid padding pollyfill ----------------------*/
.dynamic-cell-detail
{
z-index: 10000;
position: absolute;
background-color: #F4DFFA;
margin: 0;
padding: 0;
width: 100%;
display: table;
}
.dynamic-cell-detail > :first-child
{
display: table-cell;
vertical-align: middle;
text-align: center;
font-size: 12px;
line-height: 13px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/Celebio/SlickGrid/master/lib/jquery.event.drag-2.2.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/Celebio/SlickGrid/master/slick.core.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/Celebio/SlickGrid/master/slick.grid.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/Celebio/SlickGrid/master/slick.dataview.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/Celebio/SlickGrid/master/slick.grid.css">
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/Celebio/SlickGrid/master/slick-default-theme.css">
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/themes/base/jquery-ui.css">
<div id="grid-simple"></div>
Less than 200 lines of codes.
fiddle with it!
Incidentally, this is the kind of approach that the also excellent Datatables provides (almost) natively through it's API. &imo it is the correct pattern; & how i am choosing to implement my own stuff using Slickgrid
. But it involves a slight hack and in any case does *not exactly meet the OP requirements; which i claim is possible.
To do dynamic row-heights per-cell, we employ a similar trick but we must also deal with a few side-effects:~
styling away the row divisions
We must:
- escape the per-cell overflow clipping
- remove unwanted row stipeyness
- remove row borders
The Slickgrid API
provides row-based styling via the Grid.getItemMetadata callback interface. In the next fiddle, on line 107, see the onRenderRow
implementation of this interface:
fiddle with it!
Notice also on lines 148-150, i invoke the Slickgrid
Grid.setCellCssStyles API to add a custom dynamic-cell
css
class that sets the overflow
to visible
in order to style away the per-cell overflow clipping.
column resizing
If the detail content is static, column resizing is a gimme.
Detail content that responds to a change in column width (flowing text or wotnot) requires some work. Padding rows need to be dynamically added and removed accordingly. See (from line 66) the addPadding
and trimPadding
functions in the next fiddle:
fiddle with it!
sorting
There's some work to do here also. we need to ensure that no matter whether we are sorting up or down, the padding remains contiguously underneath the parent. See the comparer
on line 136 in this next fiddle:
fiddle with it!
filtering
Pretty much a one-liner: if it's padding, then delegate the comparison to the parent. job done. See the pcFilter
on line 192 in the next fiddle:
fiddle with it!
Yay! that's resizing, sorting & filtering in under 500 lines of fairly legible, liberally commented custom javascripts.. i have in fact seen certain fancy input-range-slider
pollyfills with more lines of code ;)
acu
Caveats
I have only covered the basics. There is that whole selectable/editable aspect to Slickgrid
,, ~which goes beyond my current requirements (sry).
Also:
- example-code only; not production ready. you have been warned etc etc =)
- examples seem to work in most modern browsers; i have /not/ tried with iE; versions >= 11 might be ok..
Further infos
There is more to be said about this than can reasonably be squeeezed into a SO
answer -notwithstanding the no-references policyguidelines. Anyone interested in knowing more about all this stuff can go here where i go into a fair bit more detail.
One final example
Here's a final fun example. it employs all of the above functionality but as can be seen, i have ditched the expando-rows and there are two dynamic content fields. Also, fyi, this example makes use of MutationObservers in order to generate onPostRender
events as an alternative to the Slickgrid
native
asyncPostRender
column option callback:
fiddle with it!
And there we have it. -some of the way towards a DataView-like Slickgrid extension-mod
; & all without needing to resort to horrid hax on the lovely Slickgrid codes. yippee ;)
回答2:
You can do this via css.
Take a look at the slick.grid.css
file, and make your desired changes there.
Take a look at the
.slick-row.ui-widget-content
, .slick-row.ui-state-active
classes
OR
You can use the rowHeight
property of the SlickGrid
take a look at the grid options
https://github.com/mleibman/SlickGrid/wiki/Grid-Options
回答3:
I attached a function to my grid which enables me to expand the row height:
var normalHeight = 25;
var expandedHeight = 100;
var columns = [
{id: "col1", name: "Column 1", field: "col1", expanded: false},
{id: "col2", name: "Column 2", field: "col2"}
];
var options = {
rowHeight: normalHeight
};
var dataView = new Slick.Data.DataView();
var grid = new Slick.Grid(element, dataView, columns, options);
grid.updateOptions = function(expanded){
var columns = grid.getColumns();
if(!expanded){
options['rowHeight'] = normalHeight;
columns[0]['expanded'] = false;
}else{
options['rowHeight'] = expandedHeight;
columns[0]['expanded'] = true;
}
grid.setOptions(options);
grid.setColumns(columns);
grid.invalidate();
grid.render();
}
It seems to work nicely, hopefully this is helpful to someone.
来源:https://stackoverflow.com/questions/10535164/can-slickgrids-row-height-be-dynamically-altered