Format Column Header on pivot table as date in ag-grid - ag-grid

I am using a valueFormatter for my date columns in ag-grid (react) that looks like this:
columnTypes: {
"dateColumn": {
filter: 'agDateColumnFilter',
enableRowGroup: true,
valueFormatter: Blotter.formatDate,
enablePivot: true,
enableValue: true
}
}
where Blotter.formatDate is a simple formatter that outputs the date in the user's preferred format. However, a common requirement in my application is to build pivot tables using a date series so the date column ends up in the "Column Labels" section of the pivot definitions. When I do this, the date column header shows the full, unformatted date and is also not sorted in any particular order. How can I make it so that the column label formats the dates in a reasonable way and have them sorted incrementing from left to right?
Here's what I'm seeing:
The dates across the top are from the "Position Date" column, which renders like this when pivot is not turned on:
Thankyou,
Troy

I was stuck with the same problem. I finally found the solution.
You must set the keyCreator on you column definition

As Olivier mentioned you can use the keyCreator property for your purpose.
But be aware when you use it together with valueFormatter, it causes a strange behaviour in your Pivot.
Here a link to the official documentation where you can find out more
https://www.ag-grid.com/angular-grid/column-properties/

processSecondaryColDef
"This is a callback that gets called on each group definition":
processSecondaryColGroupDef
.
Using the method above you can resolve the problem.
I fixed the same issue in my project.
From the link:
// This is a callback that gets called on each column definition
processSecondaryColDef: function (colDef) {
colDef.headerName = colDef.headerName.toUpperCase();
},
processSecondaryColGroupDef: function (colGroupDef) {
if (colGroupDef.pivotKeys[0] === '2002') {
colGroupDef.headerClass = 'color-background';
}
colGroupDef.headerName = 'Year ' + colGroupDef.headerName;
},
};

Related

AgGridReact component not updating when autoGroupColumnDef changes

I'm using ag-Grid with React to test their enterprise row grouping feature. I want to change the row grouping column name during runtime, but I haven't been able to do this.
When I make changes to AgGridReact's columnDefs prop, the changes are reflected in the table. But changes to the autoGroupColumnDef prop aren't rendered, even though the debug logs show that the change was detected. Here's an example in TypeScript (using React hooks for state):
import React, { FC, useState } from 'react';
import 'ag-grid-enterprise';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham.css';
const AgGridTest: FC = () => {
const rowData = [
{ col1: 'a', col2: 0, col3: 0 },
{ col1: 'b', col2: 1, col3: 1 },
{ col1: 'c', col2: 2, col3: 0 },
{ col1: 'd', col2: 3, col3: 1 },
{ col1: 'a', col2: 4, col3: 0 },
{ col1: 'b', col2: 5, col3: 1 },
{ col1: 'c', col2: 6, col3: 0 },
{ col1: 'd', col2: 7, col3: 1 },
];
const [columnDefs, setColumnDefs] = useState([
{ headerName: 'Column 1', field: 'col1', rowGroup: true }, // initial group column
{ headerName: 'Column 2', field: 'col2', rowGroup: false },
{ headerName: 'Column 3', field: 'col3', rowGroup: false },
]);
const [autoGroupColumnDef, setAutoGroupColumnDef] = useState(
{headerName: 'col1 Initial'} // auto group column name is 'col1 Initial' to start
);
const groupByColumn = (field: string): void => {
// this successfully changes the grouping column...
setColumnDefs(
prevColumnDefs => prevColumnDefs.map(
colDef => colDef.field === field ?
{...colDef, rowGroup: true} :
{...colDef, rowGroup: false}
)
);
// ...but this won't change the auto group column name!
setAutoGroupColumnDef({
headerName: `${field} Group Column`,
});
}
return (
<div className="ag-theme-balham" style={{ height: '300px' }}>
<button onClick={() => groupByColumn('col1')}>Group by Column 1</button>
<button onClick={() => groupByColumn('col2')}>Group by Column 2</button>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
autoGroupColumnDef={autoGroupColumnDef}
debug // enable debug logs
/>
</div>
);
}
const App: React.FC = () => <AgGridTest />
export default App;
I tried calling the grid API's api.refreshHeader but that didn't work either:
const AgGridTest: FC = () => {
// ...
// store the api in an instance variable
// when the on ready event makes it available
const api = useRef<GridApi>();
const onGridReady = (params: GridReadyEvent) => {
api.current = params.api;
}
const groupByColumn = (field: string): void => {
// ... re-assign the grouping cols ...
}
useEffect(() => {
// run this after grouping changes have been rendered
gridApi.current && gridApi.current.refreshHeader(); // doesn't work
});
return (
<div>
<AgGridReact
onGridReady={onGridReady}
// ...
/>
</div>
);
}
I tried looking for an API function to set the autoColumnGroupDef but I couldn't find one. The documentation only mentions setting properties through gridOptions. The API has a function for updating regular column definitions: api.setColumnDefs. This is what AgGridReact calls when the columnDefs prop changes (relevant code snippets here and here). But when the autoGroupColumnDef property is changed, AgGridReact simply overwrites the autoGroupColumnDef property in the gridOptions object (relevant code snippet). There doesn't seem to be any associated setter function.
There doesn't even seem to be a way to get at the auto group column objects. columnApi.getAllColumns/getColumnGroup only return regular columns. The auto group column list is kept separately in a private instance variable in columnController (relevant code snippet), which has a public getPrimaryAndSecondaryAndAutoColumns accessor exposed, except that we can't access the columnController API.
Any ideas on how can I modify the auto group column names at runtime? Or do I have to disable them and create my own group columns?
This question was asked here and here but they never received answers (and I don't understand the comment in the second question).
As you are probably aware, ag-grid is pure js, and the React and Angular versions are wrappers around that.
I've been using the Angular version for about a year, and customizing it heavily, and I've found that many of the gridOptions, even though they are bound properties, have no effect after initial instantiation of the grid. I suspect that autoGroupColumnDef is one of these.
So, I would try creating your own column groups, as per the link that you referenced.
If that doesn't work, the (ugly) alternative is to destroy the grid, and then recreate it with the new gridOptions reflecting your new autoGroupColumnDef
One other hard-won piece of advice:
If you use the setColumnDefs API to update the column definitions, it works fine, BUT, if you later want save and restore the grid state, getting the grid state from the API yields column names with "_1" appended to them, and if you set the column state with that, you get an error about the columns not being found. The solution is to set the columnDefs to an empty array first, and then set your actual columnDefs.
#GreyBeardedGeek mentioned that using the setColumnDefs API mutates the column names. This sent me down a rabbit hole which may prove useful to others.
tl;dr
If you want to change column order, width, sort state, filtering, row grouping, or pivoting at runtime through the API (e.g. api.setColumnDefs) instead of only through ag-Grid's UI controls (e.g. allowing the user to resize columns by clicking and dragging), you must:
Specify a unique colId for each column.
Set gridOptions.deltaColumnMode to true.
If you want to also allow the user to modify these properties (e.g. click-n-drag resize), add an explicit handler that watches for these changes and updates the columnDefs accordingly.
I'll now explain why (sort of - some parts are still unclear to me).
If you provide colIds but leave deltaColumnMode false...
I made a plunkr where you can create a new column and update the new column and an existing column's sort order, width, and whether it's a row grouping column. The first thing to note is that the initial width in the columnDefs for column 1 is applied, but clicking "increase width" for either the existing or new column does nothing. Grouping doesn't work either - the cell renderer will change, centering the content, but no grouping will actually be performed.
This is because deltaColumnMode is false by default, and when deltaColumnMode is false, runtime changes to certain properties (which include width and row grouping) are ignored. From the docs:
By default when new columns are loaded into the grid, the following properties are not used:
Column Order
Aggregation Function (colDef.aggFunc)
Width (colDef.width)
Pivot (colDef.pivot or colDef.pivotIndex)
Row Group (colDef.rowGroup or colDef.rowGroupIndex)
Pinned (colDef.pinned)
This is done on purpose to avoid unexpected behaviour for the application user.
For example - suppose the application user rearranges the order of the columns. Then if the application sets new column definitions for the purposes of adding one extra column into the grid, then it would be a bad user experience to reset the order of all the columns.
Similarly, when we create the new column, we place it first in the list of columnDefs, but this order is ignored and it's instead created at the end. This is because column order is one of the properties ignored when deltaColumnMode is false.
The second thing to note is that sort order is preserved even when we make columnDefs updates. Because we provided colIds, ag-Grid knew that when we changed the columnDefs, we were updating existing columns and not creating new ones. ag-Grid preserves internal state like sort order and row grouping when the colIds are the same. From the docs:
Comparison of column definitions is done on 1) object reference comparison and 2) column ID eg colDef.colId. If either the object reference matches, or the column ID matches, then the grid treats the columns as the same column. For example if the grid has a column with ID 'country' and the user sets new columns, one of which also has ID of 'country', then the old country column is kept in place of the new one keeping it's internal state such as width, position, sort and filter.
If you don't provide any colIds and leave deltaColumnMode false...
If you don't provide colIds in the initial columnDefs, ag-Grid will assign them internally:
If the user provides colId in the column definition, then this is used, otherwise the field is used. If both colId and field then colId gets preference. If neither colId or field then numeric is provided. Then finally the ID ensured to be unique by appending '_[n]' where n is the first positive number that allows uniqueness.
Whenever columnDefs change, ag-Grid decides whether the changes are to an existing column or to a new column by checking the colIds (or column object references). From the docs:
If you are updating the columns (not replacing the entire set) then you must either provide column ID's or reuse the column definition object instances. Otherwise the grid will not know that the columns are in fact the same columns.
When we provided colIds, ag-Grid knew we were updating existing columns. But when we don't provide colIds, ag-Grid doesn't know what to do. Let's look at the same plunkr as before but with colIds removed. Now, when we click the button to increase column 1's width, three things happen: 1) column 1's width does increase, but 2) its order shifts so it's now the rightmost column, and 3) if we keep clicking "increase width", we see in the console that column 1's colId was initially undefined, becomes "col1" (the field name) after the first increase, and then alternates between "col1_1" and "col1" on every other increase.
I suspect that this strange behavior is due to the way ag-Grid resolves columnDefs updates when colIds aren't provided. Because no colId is provided, ag-Grid seems to think that we're creating a new column when we update column 1's width. Since ag-Grid thinks this is a new column, the width change is actually applied, even though deltaColumnMode is false (remember that in the previous example with colIds set, the width property was applied when the column was created, just not during runtime). But because deltaColumnMode is false, when we create a new column, its order is ignored - hence, column 1 is placed as the rightmost column. (Though, since the docs say that width/column order should be ignored "when new columns are loaded into the grid", I don't quite know why the initial width is applied but column order is not).
Similar things happen when we group by column 1, but 1) all colIds change for all columns, and 2) the grouping column is moved to be the leftmost column. I suspect the first is because all columns change when grouping is applied, and the second is because the grouping column is moved to the left by default, as when we create a new column and group by it, it'll be moved to the left, too.
You may notice that sometimes after making changes to either column 1 or the new column, you're not able to apply any sorting to them. Remember how ag-Grid alternates between appending and removing a "_1" to the colIds? Since api.setSortModel looks up columns by colIds, every time the colId has a "_1" appended to it, api.setSortModel won't be able to find the column to apply sorting (and will fail silently, apparently). We could fix this by getting the current colIds with columnApi.getAllColumns() and using those instead.
Also, since no colIds are provided, ag-Grid doesn't preserve column state (e.g. sort order) between columnDef changes. If you sort a column and then increase its width or group by it, the sorting will be reset. This is because sorting is attached to the internal state of the column object, but the column objects are destroyed when the columnDefs change.
If you provide colIds and set deltaColumnMode to true...
Here's a plunkr to demonstrate. Now, things work as we expect: runtime changes are applied, the new column is created in the leftmost position, and sort order is preserved between columnDef updates. However, there's a reason that deltaColumnMode is false by default. Again, from the docs (emphasis mine):
This is done on purpose to avoid unexpected behaviour for the application user.
For example - suppose the application user rearranges the order of the columns. Then if the application sets new column definitions for the purposes of adding one extra column into the grid, then it would be a bad user experience to reset the order of all the columns.
Likewise if the user changes an aggregation function, or the width of a column, or whether a column was pinned, all of these changes the user does should not get undone because the application decided to update the column definitions.
To change this behaviour and have column attributes above (order, width, row group etc) take effect each time the application updates the grid columns, then set gridOption.deltaColumnMode=true. The responsibility is then on your application to make sure the provided column definitions are in sync with what is in the grid if you don't want undesired visible changes - eg if the user changes the width of a column, the application should listen to the grid event columnWidthChanged and update the applications column definition with the new width - otherwise the width will reset back to the default after the application updates the column definitions into the grid.
Try dragging column 1 to a different order and then increasing its width. You'll see that its order gets reset to its original order in the columnDefs, just as the docs say. To preserve column order, you need to set a listener for the column drag event and update the columnDefs with the new order - see this plunkr and the onDragStopped handler for an example.
And if you want to allow the user to change aggregation functions, width, pivoting, row grouping, or pinning through the ag-Grid default UI controls, you'll need to add listeners for these changes, too.
I've been tackling this issue for a while, and I finally found a solution (albeit a bit out of the box, but it works)
If the only thing you want to change is the header, you can use dom manipulation. You just have to get the exact element that holds the title that you wish to change. In my case, I wanted to show the list of selected aggregations in the order they were selected, and so I was able to accomplish it via:
aggregateMyColumn() {
this.gridColumnApi.setRowGroupColumns(this.orderedAggregationFields);
if(this.orderedAggregationFields.length > 0) {
const container = document.querySelector("#agGrid");
const matches = container.querySelector("div[col-id='ag-Grid-AutoColumn']");
const final = container.querySelector("span[role='columnheader']");
this.renderer.setProperty(final, 'innerHTML', this.orderedAggregationFields.toString());
}
}
Note: there are reasons to use renderer2 to set properties/change innerhtml (something something security), here are some useful links though:
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
https://www.ninjadevcorner.com/2019/04/dom-manipulation-using-angular-renderer2.html

Wanna disable the column (row no:) from sorting usage by Tabulator js plugin

How can I make all columns on a Tabulator table auto sort except the first column
I have tried sortable:false & headerSort:false.
img(1) Initial table
img(2) Sorting table at Name: column.
(Target : Remaining the No: value from 1 to 6 by ascending order even Name: order change.)
Could you please help me to find a solution.
Thanks.
As per;
https://github.com/olifolkerd/tabulator/issues/861
"You need to set the headerSort property in the column definition object for the column you want to not be sortable, not on the table as a whole. the sortable property you are currently using in your column definition was removed in version 3.0"
$("#mytable").tabulator({
height:205, // Set height of table, this enables the Virtual DOM and improves render speed
//layout:"fitColumns", // Fit columns to width of table (optional)
resizableColumns:false, // Disable column resize
responsiveLayout:true, // Enable responsive layouts
placeholder:"No Data Available", // Display message to user on empty table
initialSort:[ // Define the sort order:
{column:"altitude", dir:"asc"}, // 1'st // THIS IS WHAT YOU'RE LOOKING FOR I ASSUMEN
],
columns:[
{title:"Flight", field:"flight", headerSort:false, responsive:0, align:"left"}, // , width:250},
{title:"CallSig", field:"callsign", headerSort:false, responsive:3},
...
Further reading: http://tabulator.info/docs/3.3#sorting
EDIT: You can set sorting programmatically;
$("#example-table").tabulator("setSort", "age", "asc");
Hope this helps.

ag grid : js : How to find which filter is applied

I have data displayed in ag grid.
It has 4 columns name,age,sport and has the following data:-
data
now when I filter the data based on "sport" , lets say uncheck "blanks"
I get the following data:-
filtered data
1)My questions is how to get the which filter is applied on columns ,
like in this case "blanks" is applied on "sport".
2)If I refresh the data using
api.setRowdata(), filter applied to the columns are lost
1.how to get the which filter is applied on columns
You can get it using gridApi.filterManager.allFilters
Check this live example ag-grid: Built-In Filters - log the filters applied.
Apply age and year filters and then click Log Filter button.
age: {column: Column, filterPromise: Promise, scope: null, compiledElement: null, guiPromise: {…}}
year: {column: Column, filterPromise: Promise, scope: null, compiledElement: null, guiPromise: {…}}
__proto__: Object
Starting from ag-grid#19.0.0 to get or set all applied filters you can take advantage of using:
const savedModel = gridApi.getFilterModel();
gridApi.setFilterModel(savedModel);
For the
if I refresh the data using api.setRowdata(), filter applied to the columns are lost
we can use the following settings for columns in columnDefs object :-
filterParams: {
newRowsAction: 'keep'
}

How to hide a row in RDLC when particular field value matches with some string value?

For Ex:In RDLC, I have a Grid with the following columns
period,
Quantity,
description etc..
I will get Period values as dates and one value as "Avg", Now when My Period Value is "Avg", I want to hide that row from Grid in RDLC report.how can i do this?
I tried like below:
Row Visibility -> Show or Hide Based on Expression ->
(Fields!Period.Value = "Avg", True, False). But it is not working.
Please help me in this regard....
I would recommend to check actual values of Period field. Also, expression for Visibility in this case should start with equal sign and IIF (in case you didn't just copied it partially):
= IIF(Fields!Period.Value = "Avg", True, False)

XPath - is a date null

Using XPath, how do I figure out if a date or datetime field is null or blank?
I am using the concat method as a stand-in for the XPath if statement
concat(
substring(../preceding-sibling::my:PerDiem[1]/my:perDiemEnd, 1, ../preceding-sibling::my:PerDiem[1]/my:perDiemEnd = "" * string-length(../preceding-sibling::my:PerDiem[1]/my:perDiemEnd)),
substring(/my:ExpenseForm/my:ExpenseHeader/my:departureDateTime, 1, not(../preceding-sibling::my:PerDiem[1]/my:perDiemEnd = "") * string-length(/my:ExpenseForm/my:ExpenseHeader/my:departureDateTime))
)
More info:
In Infopath 2010, a repeating table has two date/time fields called perDiemStart and perDiemEnd. In the repeating table, the next perDiemStart is the previous perDiemEnd. This is easily done if the default value of perDiemStart is ../preceding-sibling::my:PerDiem[1]/my:perDiemEnd
But for the first perDiemStart (since a previous perDiemEnd does not exist, I suppose it would be null/blank). I want that first (blank) value to be a different: value of departureDateTime node
Node locations:
/my:ExpenseForm/my:ExpenseHeader/my:departureDateTime
/my:ExpenseForm/my:PerDiemDetails/my:PerDiems/my:PerDiem/my:perDiemStart
/my:ExpenseForm/my:PerDiemDetails/my:PerDiems/my:PerDiem/my:perDiemEnd
To check if it is filled:
perDiemStart[text()]
To check if it is empty/null:
perDiemStart[not(text())]
Does this help? http://blogs.msdn.com/b/syamp/archive/2011/03/13/fim-2010-xpath-how-to-check-null-value-on-a-datetime-attribute.aspx
Basically they detect null dates by getting the set of dates after an old date (e.g. 1900-01-01) and then using 'not' to see which nodes would be excluded.