Slickgrid 3 grouping how to remove totals row? - javascript

I use multi-row grouping and put the totals in the grouping headers.
I'm not using the totals in the totals rows. I see the the rows are grouped and where the totals would be there are empty rows. In my case there is an empty row after each of the child grouping and at the end there is an empty row for the parent totals.
How do I remove these totals rows?
Thank you!
.cshtml:
<div id="gridList" class="grid" style="width: 100%; height: 500px"></div>
.js
$(function() {
var columns = [
{
id: "isExcluded",
name: "Exclude",
field: "isExcluded" /*, width: 120*/,
formatter: Slick.Formatters.Checkmark,
editor: Slick.Editors.Checkbox, sortable: true
},
{
id: "symbol",
name: "Symbol",
field: "symbol",
sortable: true /*, width: 120*/
},
{
id: "price",
name: "Price",
field: "price",
sortable: true
//, groupTotalsFormatter: sumTotalsFormatter
},
{
id: "parent_name",
name: "Parent Name",
field: "parent_name",
sortable: true /*, width: 120*/
},
{
id: "description",
name: "Description",
field: "description",
sortable: true,
width: 120,
editor: Slick.Editors.Text,
},
{ id: "cancel_ind",
name: "Canceled",
field: "cancel_ind",
sortable: true, width: 80 }
];
function requiredFieldValidator(value) {
if (value == null || value == undefined || !value.length) {
return { valid: false, msg: "This is a required field" };
} else {
return { valid: true, msg: null };
}
};
var options = {
editable: true,
enableAddRow: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: true,
enableExpandCollapse: true,
rowHeight: 25
};
var sortcol = "parent_name";
var sortdir = 1;
var grid;
var data = [];
var groupItemMetadataProviderTrades = new Slick.Data.GroupItemMetadataProvider();
var dataView = new Slick.Data.DataView({ groupItemMetadataProvider: groupItemMetadataProviderTrades });
dataView.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
function groupByParentAndSymbol() {
dataViewTrades.setGrouping([
{
getter: "parent_name",
formatter: function(g) {
return "Parent: " + g.value + " <span style='color:green'>(" + g.count + " items) Total: " + g.totals.sum.price + "</span>";
},
aggregators: [
new Slick.Data.Aggregators.Sum("price")
],
aggregateCollapsed: true
,lazyTotalsCalculation: true
},
{
getter: "symbol",
formatter: function(g) {
return "Symbol: " + g.value + " <span style='color:green'>(" + g.count + " items) Total: " + g.totals.sum.price + "</span>";
},
aggregators: [
new Slick.Data.Aggregators.Sum("price")
],
collapsed: true
,lazyTotalsCalculation: true
}]);
};
grid = new Slick.Grid("#gridList", dataView, columns, options);
grid.registerPlugin(groupItemMetadataProviderTrades);
grid.setSelectionModel(new Slick.RowSelectionModel());
..... /*sorting support etc*/
// use instead of the default formatter <--- removed not used.
function sumTotalsFormatter(totals, columnDef) {
var val = totals.sum && totals.sum[columnDef.field];
//if (val != null) {
// return "total: " + ((Math.round(parseFloat(val) * 100) / 100));
//}
return "";
}
// will be called on a button click (I didn't include the code as irrelevant)
var getDataList = function () {
$.getJSON('/Home/GetData/', function (json) {
data = json;
dataView.beginUpdate();
dataView.setItems(data);
groupByParentAndSymbol();
dataView.endUpdate();
dataView.syncGridSelection(grid, true);
});
};
getDataList();
});

Adding displayTOtalsRow: false to the dataview solved my problem - the total rows are not shown now.
var dataView = new Slick.Data.DataView({ groupItemMetadataProvider: groupItemMetadataProviderTrades, displayTotalsRow: false });

To answer simply to your question... Just remove the aggregators: [...], when I say remove you have 2 options, you can remove whatever is the array [...] or you could simply erase completely that object line (so removing completely the aggregators[...]).
Now if you want more explanation of how it works...Let's give you some definition so you'll understand better. An aggregate is a collection of items that are gathered together to form a total quantity. The total in question could be a sum of all the fields, an average or other type of aggregator you might define. So basically it's the action of regrouping by the column you chose and give the calculation result of the group. Alright now how does it work in the case of a SlickGrid? You need to define what kind of Aggregator you want to use (Avg, Sum, etc...) that goes inside your function groupByParentAndSymbol(), defining them will do the calculation BUT unless you attach it to the field, nothing will be displayed, so in the example you found it is very important to attach/bind this groupTotalsFormatter: sumTotalsFormatter to your columns definition, as for example:
...var columns = [{id: "cost", ...width: 90, groupTotalsFormatter: sumTotalsFormatter}, ...];
so let's recap... Once the Aggregator is define new Slick.Data.Aggregators.Sum("cost") it will do the calculation but unless it's bind to the field nothing will be displayed. As an extra option, the aggregateCollapsed (true/false) is to display the sub-total (avg, sum or whatever) inside or outside the group when you collapse.

Related

How to display different column data as tool tip in ag grid pivot mode?

var ColDef = [{
headerName: "colA",
field: 'colA',
rowGroup: true
},
{
headerName: "colB",
field: 'colB',
pivot: true,
enablePivot: true
},
{
headerName: "colC",
field: 'colC',
rowGroup: true
},
{
field: 'colD',
aggFunc: 'first',
valueFormatter: currencyFormatter,
tooltip: function(params) {
return (params.valueFormatted);
},
},
{
field: 'comment'
},
{
field: 'colF'
}
];
function currencyFormatter(params) {
return params.value;
}
above code is from different question. it works but i want to use different 'comment' field as tool tip to current 'colD' . also this is a group and pivot agGrid,if it is normal grid this is not a problem. I would appreciate any ideas for group and pivot agGrid?
Not sure if there is good way for the grid to get the data in your scenario then, as your rows and columns are different than original model after pivot.
Maybe you can consider retrieve this information outside of grid. Assume you also want some aggregated information displays in the tooltip, the tooltip function may eventually look like this:
tooltip: params => {
const country = params.node.key;
const year = params.colDef.pivotKeys[0];
const athletesWithNumbers = this.state.rowData
.filter(d => d.year == year)
.filter(d => d.country === country)
.filter(d => d.gold > 0)
.map(d => d.athlete + ': ' + d.gold);
return athletesWithNumbers.join(', ');
}
See this plunker for what I am talking about - again, not sure if this is what you want but just an FYI.
just use tooltipValueGetter
{
field: 'message',
headerName: 'Message',
headerTooltip: 'Message',
width: 110,
filter: 'agSetColumnFilter',
tooltipValueGetter: (params) => `${params.value} some text`
}
or just use the same method for tooltipValueGetter
UPDATE:
Okay, I understood
but it also easy
Ag-grid has property tooltipField - where you can choose any field from grid
For example here - in the column of 'sport' I am showing tooltip of the previous column
Example: https://plnkr.co/edit/zNbMPT5HOB9yqI08
OR
You can easily manipulate with data for each field by tooltipValueGetter
with next construction:
tooltipValueGetter: function(params) {
return `Country: ${params.data.country}, Athlete: ${params.data.athlete}, Sport: ${params.data.sport}`;
},
Example: https://plnkr.co/edit/zNbMPT5HOB9yqI08
Result:
UPDATE 2
Hey Man! I do not understand was is wrong
I just used your code snippet and my solution
And it works as you want
Example: https://plnkr.co/edit/zNbMPT5HOB9yqI08
UPDATE 3
A little bit of manipulation and I can get the data
{ field: 'gold', aggFunc: 'sum',
tooltipValueGetter: function(params) {
var model = params.api.getDisplayedRowAtIndex(params.rowIndex);
return model.allLeafChildren[0].data.silver;
},
},
The Last:
https://plnkr.co/edit/9qtYjkngKJg6Ihwb?preview
var ColDef = [{
headerName: "colA",
field: 'colA',
rowGroup: true
},
{
headerName: "colB",
field: 'colB',
pivot: true,
enablePivot: true
},
{
headerName: "colC",
field: 'colC',
rowGroup: true
},
{
field: 'colD',
aggFunc: 'last',
tooltipValueGetter: commentTooltipValueGetter
},
{
field: 'comment'
},
{
field: 'colF'
}
];
function commentTooltipValueGetter(params) {
const colB = params.colDef.pivotKeys[0];
var model = params.api.getDisplayedRowAtIndex(params.rowIndex);
for (var i = 0; i < model.allLeafChildren.length ; i++) {
if (model.allLeafChildren[i].data.colB=== colB) {
return model.allLeafChildren[i].data.comments;
}
}
}
This is what i had to do for my question. It is combination of answers from #wctiger and #shuts below. So please also refer them for more context

Binding array of object to Kendo grid popup multiselect

I'm trying to bind an array of id-value pairs to a kendo grid popup editor.
Got everything to work for creating a new record. Popup editor loads the custom editor and successfully submits the data to the controller.
The problem is when I try to edit records. The records displays properly in the row, but when I try to edit it, the multiselect does not hold the values.
Grid Markup
$("#ProjectSites-SubContract-grid").kendoGrid({
dataSource: {
type: "json",
schema: {
data: "Data",
total: "Total",
errors: "Errors",
model: {
id: "Id",
fields: {
DateOfContract: { type: 'date', editable: true },
DateOfCompletion: { type: 'date', editable: true },
AmountOfContract: { type: 'number', editable: true },
Contractor: { defaultValue: { id: "", name: "" } }
}
}
},
},
columns: [
{
field: "ScopeOfWork",
title: "Scope of Work",
template: "#=parseScopeOfWork(ScopeOfWork)#",
editor: scopeOfWorkEditor
},
]
});
});
Scope of Work editor
function scopeOfWorkEditor(container, options) {
$('<input data-text-field="name" data-value-field="id" data-bind="value:ScopeOfWork"/>')
.appendTo(container)
.kendoMultiSelect({
dataSource: {
data: [
#foreach (var scopeOfWork in Model.AvailableScopeOfWork)
{
<text>{ id : "#scopeOfWork.Value", name : "#scopeOfWork.Text" },</text>
},
]
}
});
parseScopeOfWork -
this method guys iterates through the object list and concats the name.
function parseScopeOfWork(scopeOfWork) {
var result = "";
for (var i = 0; i < scopeOfWork.length; i++) {
result += scopeOfWork[i].Name;
if (i < scopeOfWork.length - 1)
{
result += ", <br/>";
}
}
return result;
}
Here's a screenshot:
You're binding the SpaceOfWork to the new widget, but how that widget knows your Model ? I mean, just using data-bind doens't binds the model to the widget, it can't figure that by itself. I have two suggestions:
Set the value in the widget's initialization:
.kendoMultiSelect({
value: options.model.ScopeOfWork
Demo
Bind the model to the widget for good:
let $multiSelect = $('<input data-text-field="name" data-value-field="id" data-bind="value:ScopeOfWork"/>');
kendo.bind($multiSelect, options.model);
$multiSelect
.appendTo(container)
.kendoMultiSelect({ ...
Demo
Note: Edit the category cell in both demos to see the changes.

One Column Value remain unchanged in kendo grid drag and drop

I'm fairly new to kendo UI but some how I managed to render a kendo grid with drag and drop feature Where users can drag and place rows.In my case I have three columns id,name,sequence
So I need to keep sequence column data unchanged while id and name data changed when a drag and drop of a row.
Ex id=1 Name=David Sequnce=0
id=2 Name=Mark Sequnce=1
Now I'm going to drag row 1 to 2 while data of the sequence column remain unchanged new data like this,
Ex id=2 Name=Mark Sequnce=0
id=1 Name=David Sequnce=1
In my case every row is getting changed. I need to implement this solution.
Can somebody help me out on this.
Cheers,
Chinthaka
Try this,
Script
<script type="text/javascript">
$(document).ready(function () {
var data = [
{ id: 1, text: "David ", Sequnce: 0 },
{ id: 2, text: "Mark ", Sequnce: 1 }
]
var dataSource = new kendo.data.DataSource({
data: data,
schema: {
model: {
id: "id",
fields: {
id: { type: "number" },
text: { type: "string" },
Sequnce: { type: "number" }
}
}
}
});
var grid = $("#grid").kendoGrid({
dataSource: dataSource,
scrollable: false,
columns: ["id", "text", "Sequnce"]
}).data("kendoGrid");
grid.table.kendoDraggable({
filter: "tbody > tr",
group: "gridGroup",
hint: function (e) {
return $('<div class="k-grid k-widget"><table><tbody><tr>' + e.html() + '</tr></tbody></table></div>');
}
});
grid.table/*.find("tbody > tr")*/.kendoDropTarget({
group: "gridGroup",
drop: function (e) {
var target = dataSource.get($(e.draggable.currentTarget).data("id"));
dest = $(e.target);
if (dest.is("th")) {
return;
}
dest = dataSource.get(dest.parent().data("id"));
//not on same item
if (target.get("id") !== dest.get("id")) {
//reorder the items
var tmp = target.get("Sequnce");
target.set("Sequnce", dest.get("Sequnce"));
dest.set("Sequnce", tmp);
dataSource.sort({ field: "Sequnce", dir: "asc" });
}
}
});
});
</script>
View
<div id="grid">
</div>
Demo: http://jsfiddle.net/nmB69/710/

Kendo Grid Draggable Rows With Kendo Template

I'm going to implement drag and drop behaviour with kendo grid which is populated using template. How can I achieve draggable rows and reordering with kendo grid.
.Orderable()
Works a treat. Maybe try ".Dragable()" I'm a bit unsure about that though.
Take a look at following my demo code and try it to implement.
var data = [
{ id: 1, text: "text 1", position: 0 },
{ id: 2, text: "text 2", position: 1 },
{ id: 3, text: "text 3", position: 2 }
]
var dataSource = new kendo.data.DataSource({
data: data,
schema: {
model: {
id: "id",
fields: {
id: { type: "number" },
text: { type: "string" },
position: { type: "number" }
}
}
}
});
var grid = $("#grid").kendoGrid({
dataSource: dataSource,
scrollable: false,
columns: ["id", "text", "position"]
}).data("kendoGrid");
grid.table.kendoDraggable({
filter: "tbody > tr",
group: "gridGroup",
hint: function(e) {
return $('<div class="k-grid k-widget"><table><tbody><tr>' + e.html() + '</tr></tbody></table></div>');
}
});
grid.table/*.find("tbody > tr")*/.kendoDropTarget({
group: "gridGroup",
drop: function(e) {
var target = dataSource.get($(e.draggable.currentTarget).data("id")),
dest = $(e.target);
if (dest.is("th")) {
return;
}
dest = dataSource.get(dest.parent().data("id"));
//not on same item
if (target.get("id") !== dest.get("id")) {
//reorder the items
var tmp = target.get("position");
target.set("position", dest.get("position"));
dest.set("position", tmp);
dataSource.sort({ field: "position", dir: "asc" });
}
}
});
put .Dragable()
but make sure that you sit it in the right place, the ordering is required. Some times you may not get the expected result and that may happen due to not paying attention to the order.

custom formatter for grouptext in jqgrid

Is there a way I can use custom formatter to format the grouptext value in jqgrid.
Current Output for var grouping=new Array("Environment", "System", "IIR","Userlimit","Kernel Parameters");
Suppose I have these groups.
var grouping=new Array("3Environment", "0System", "4IIR","2Userlimit","1Kernel Parameters");
If I sort it in ascending order it should display System, Kernel, Userlimit, Environment, IIR i.e., using some kind of custom formatter remove 01234 from 1st character or similar to sort my groups in some already decided order.
jqGrid Code
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
var gridDiff = $("#list2");
gridDiff.jqGrid({
datastr: compareData,
datatype: "jsonstring",
colNames: ['KeyName', 'SubCategory', starheader, header1,'isEqual'],
colModel: [
{ name: 'elementName', index: 'elementName', key: true, width: 120 },
{ name: 'subCategory', index: 'subCategory', width: 1 },
{ name: 'firstValue', index: 'firstValue', width: 310, jsonmap:'attribute.0.firstValue' },
{ name: 'secondValue', index: 'secondValue', width: 310,jsonmap:'attribute.0.secondValue' },
{ name: 'isEqual', index: 'isEqual', width: 1,hidden:true}
],
pager: '#gridpager3',
rowNum:60,
scrollOffset:1,
//viewrecords: true,
jsonReader: {
repeatitems: false,
page: function(){return 1;},
root: "response"
},
//rownumbers: true,
height: '320',
autowidth:true,
grouping: true,
groupingView: {
groupField: ['subCategory'],
groupOrder: ['desc'],
groupDataSorted : false,
groupColumnShow: [false],
//groupCollapse: true,
groupText: ['<b>{0}</b>']
},
loadComplete: function() {
if (this.p.datatype !== 'local') {
setTimeout(function () {
gridDiff.trigger('reloadGrid');
}, 0);
} else {
$("#compareParentDiv").show();
}
var i, names=this.p.groupingView.sortnames[0], l = names.length;
data = this.p.data, rows = this.rows, item;
for (i=0;i<l;i++) {
if ($.inArray(names[i],grouping) >= 0) {
$(this).jqGrid('groupingToggle',this.id+"ghead_"+i);
$(rows.namedItem(this.id+"ghead_"+i)).find("span.ui-icon").click(function(){
var len = data.length, iRow;
for (iRow=0;iRow<len;iRow++) {
item = data[iRow];
if (item.isEqual) {
$(rows.namedItem(item._id_)).hide();
}
}
});
} else {
// hide the grouping row
$('#'+this.id+"ghead_"+i).hide();
}
//console.info($('#'+this.id+"ghead_"+i));
}
var i, names=this.p.groupingView.sortnames[0], l = names.length,
data = this.p.data, rows = this.rows, item;
l = data.length;
for (i=0;i<l;i++) {
item = data[i];
if (!item.isEqual) {
$(rows.namedItem(item._id_))
.css({
"background-color": "#FFE3EA",
"background-image": "none"
});
} else {
$(rows.namedItem(item._id_)).hide();
}
}
}
});
gridDiff.jqGrid('navGrid', '#gridpager3', { add: false, edit: false, del: false, search: false, refresh: false });
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Toggle",title:"Toggle Search Toolbar", buttonicon :'ui-icon-pin-s',
onClickButton:function(){
gridDiff[0].toggleToolbar();
}
});
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Clear",title:"Clear Search",buttonicon :'ui-icon-refresh',
onClickButton:function(){
gridDiff[0].clearToolbar();
}
});
gridDiff.jqGrid('filterToolbar',
{stringResult: true, searchOnEnter: false, defaultSearch: 'cn'});
Update
Or is there a way to use positioning to place each grouped item, if sorting is not an option
Updated after accepting answer by Oleg
Page1
Page3
It seems to me that you have not the problem with the custom formatter of the grouptext
You use groupingView having groupDataSorted: false so the sorting of the group (in your case groupField: ['subCategory'] with groupOrder: ['asc']) do jqGrid for you. So the standard sorting behavior will be used.
jqGrid supports sorttype property of the colModel which define how the column should be sorted. If you need custom sorting order you should define sorttype property as a function which returns integer or string used instead of the cell value from the column. the prototype of the sorttype function could be function(cellValue,rowData) so it is possible to define the order which not only the cell value of the sorted column will be used but the whole contain of the row inclusive _id_ property. In you case the usage of the first cellValue parameter would be enough.
So to solve you problem you can for example define an array with the order of 'subCategory' values which you need:
var orderOfSubCategory = [
"System", "system", "Kernel", "Kernel Parameters", "Userlimit",
"Environment", "IIR", "Product"
];
and then define 'subCategory' column as following:
{
name: 'subCategory',
index: 'subCategory',
width: 1,
sorttype: function (value) {
return $.inArray(value, orderOfSubCategory);
}
}
You should don't forget, that jQuery.inArray return 0-based index of the item or -1 if the item will be not found in the array. So the values of 'subCategory' which could be not found in the orderOfSubCategory array will be placed the first. See here the corresponding demo.
Some other small remarks about your code. You use new Array(...) which is bad practice in JavaScript instead of that you should use always [...] construct which do the same is shorter and work quickly. Example: var grouping = ["Environment", "System", "IIR","Userlimit","Kernel Parameters"];
Another place which is difficult for to read for me is:
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
Here you first use cellspacing="0" cellpadding="0" attributes of the table which are unneeded and seconds use empty(), append() combination instead of one html() call. The following code do the same, but it seems me better:
$('#compareContent').html(
'<div id="compareParentDiv" style="display: none" width="100%">' +
'<table id="list2"></table><div id="gridpager3"></div></div>');

Categories