ag-grid programmatically selecting row does not highlight - ag-grid

Using Angular 4 (typescript), I have some code like below using ag-grid 12.0.2. All I'm trying to do is load my grid and automatically (programmatically) select the first row.
:
this.gridOptions = ....
suppressCellSelection = true;
rowSelection = 'single'
:
loadRowData() {
this.rowData = [];
// build the row data array...
this.gridOptions.api.setRowData(this.rowData);
let node = this.gridOptions.api.getRowNode(...);
// console logging here shows node holds the intended row
node.setSelected(true);
// console logging here shows node.selected == true
// None of these succeeded in highlighting the first row
this.gridOptions.api.redrawRows({ rowNodes: [node] });
this.gridOptions.api.redrawRows();
this.gridOptions.api.refreshCells({ rowNodes: [node], force: true });
First node is selected but the row refuses to highlight in the grid. Otherwise, row selection by mouse works just fine. This code pattern is identical to the sample code here: https://www.ag-grid.com/javascript-grid-refresh/#gsc.tab=0 but it does not work.
Sorry I am not allowed to post the actual code.

The onGridReady means the grid is ready but the data is not.
Use the onFirstDataRendered method:
<ag-grid-angular (firstDataRendered)="onFirstDataRendered($event)">
</ag-grid-angular>
onFirstDataRendered(params) {
this.gridApi.getDisplayedRowAtIndex(0).setSelected(true);
}
This will automatically select the top row in the grid.

I had a similar issue, and came to the conclusion that onGridReady() was called before the rows were loaded. Just because the grid is ready doesn't mean your rows are ready.(I'm using ag-grid community version 19) The solution is to setup your api event handlers after your data has loaded. For demonstration purposes, I'll use a simple setTimeout(), to ensure some duration of time has passed before I interact with the grid. In real life you'll want to use some callback that gets fired when your data is loaded.
My requirement was that the handler resizes the grid on window resize (not relevant to you), and that clicking or navigating to a cell highlights the entire row (relevant to you), and I also noticed that the row associated with the selected cell was not being highlighted.
setUpGridHandlers({api}){
setTimeout(()=>{
api.sizeColumnsToFit();
window.addEventListener("resize", function() {
setTimeout(function() {
api.sizeColumnsToFit();
});
});
api.addEventListener('cellFocused',({rowIndex})=>api.getDisplayedRowAtIndex(rowIndex).setSelected(true));
},5000);
}

Since you want to select the first row on page load, you can do onething in constructor. But your gridApi, should be initialized in OnGridReady($event) method
this.gridApi.forEachNode((node) => {
if (node.rowIndex === 0) {
node.setSelected(true);
}

It's setSelected(true) that does this.
We were using MasterDetail feature, its a nested grid and on expanding a row we needed to change the selection to expanded one.
Expanding a row was handled in
detailCellRendererParams: {
getDetailRowData: loadNestedData,
detailGridOptions: #nestedDetailGridOptionsFor('child'),
}
and withing loadNesteddata, we get params using we can select expanded row as
params.node.parent.setSelected(true)
Hope this helps.

Related

Ag-grid cell renderer doesn't update its rowIndex in real time?

I'm wondering how I can complete my cell renderer. The renderer adds a new row when clicking an icon, and removes it when clicked again. The problem is the invididual cell renderers row indexes do not change unless I hard refresh the entire grid (which then disables the state of the dropdown itself)
How can I complete this?
const insertIndex = params.rowIndex + 1;
this.eventListener = () => {
if (!this.eButton.classList.contains('open')) {
gridOptions.api.applyTransaction({add:[{step: this.dropdownValue, dropdownRow: true}], addIndex: insertIndex });
} else {
let rowData = [];
gridOptions.api.forEachNode(function (node) {
rowData.push(node);
});
let insertedRow = rowData[insertIndex];
gridOptions.api.applyTransaction({remove:[insertedRow.data]});
}
this.eButton.classList.toggle('open');
};
this.eButton.addEventListener('click', this.eventListener);
}
option 1: Hard refresh the grid, but I lose the 'open' status of the dropdown icon. How can i carry state over after a hard refresh?
option 2: Find a way that params.rowIndex is updated after applyTransaction.
Ive tried using the default rowGroup feature, however it hides all columns by default and I havent found a way to make it work the way I need.
The solution im after is a simple 'open/close' toggle for a single row underneath without affecting the other rows

How can I update data within Detail table and don't loose range selection and filters?

I have latest enterprise React agGrid table with Master/Detail grid. My data is fetched on the client every 5 seconds and then put immutably to the redux store. React grid component is using deltaRowDataMode={true} props and deltaRowDataMode: true in Detail options.
My master grid performs normally as expected: if I have range selected, grid would keep selection after the data updates, so would filters and visibility menu would be still opened. But Detail grid behaves differently: on data refresh selections are being removed, visibility menu closes and grid jumps if filters were changed.
I've read in docs that when I open Detail grid it's being created from scratch, but in my case I don't close Detail. Anywhere I've tried keepDetailRows=true flag, which solved problems with jumping on update and selection loss, but Detail grid doesn't update data now.
It seems there are only two possible options according to the docs https://www.ag-grid.com/javascript-grid-master-detail/#changing-data-refresh. The first one is a detail table redraws everytime a data in a master row changes and the second one is a detail row doesn't changes at all if a flag suppressRefresh is enabled. Strange decision, awful beahviour...
Update.
Hello again. I found a coupe of solutions.
The first one is to use a detailCellRendererParams in table's options and set suppressRefresh to true. It gives an opportunity to use getDetailGridInfo to get detail-table's api.
While the detail-table's refreshing is disabled, using detailGridInfo allows to set a new data to a detail-table.
useEffect(() => {
const api = gridApiRef;
api && api.forEachNode(node => {
const { detailNode, expanded } = node;
if (detailNode && expanded) {
const detailGridInfo = api.getDetailGridInfo(detailNode.id);
const rowData = detailNode.data.someData; // your nested data
if (detailGridInfo) {
detailGridInfo.api.setRowData(rowData);
}
}
});
}, [results]);
The second one is to use a custom cellRenderer, wicth is much more flexible and allows to use any content inside a cellRenderer.
In table's options set detailCellRenderer: 'yourCustomCellRendereForDetailTable.
In yourCustomCellRendereForDetailTable you can use
this.state = {
rowData: [],
}
Every cellRenderer has a refresh metod which can be used as follow.
refresh(params) {
const newData = [ ...params.data.yourSomeData];
const oldData = this.state.rowData;
if (newData.length !== oldData.length) {
this.setState({
rowData: newData,
});
}
if (newData.length === oldData.length) {
if (newData.some((elem, index) => {
return !isEqual(elem, oldData[index]);
})) {
this.setState({
rowData: newData,
});
}
}
return true;
}
Using method refresh this way gives a fully customizable approach of using a detailCellRenderer.
Note. To get a better performance with using an immutable data like a redux it needs to set immutableData to true in both main and detail tables.

How could I add custom row focus class in ag-grid

I want to control row focus process. I need to show confirm dialog on row focus change in my table.
I tried to do this with rowClassRules property, but as I understood that functionality apply classes when table rendering, after that row classes stop changing
rowClassRules = {
'custom-row-focus': (params) => {
return params.data.id === this.currentSelectedItem.id
}
}
currentSelectedItem set's when I click on the row
Found an answer in docs
https://www.ag-grid.com/javascript-grid-row-styles/#refresh-of-styles
If you refresh a row, or a cell is updated due to editing, the rowStyle, rowClass and rowClassRules are all applied again.
So, when I'm clicking to the row I should make something like that:
onClicked($event: RowClickedEvent) {
$event.node.setData({...$event.data});
}

ag-grid - how to get parent node from child grid via context menu?

As an example, I have a Master\Detail grid.
Master\Detail defined as key-relation model and on getDetailRowData method parent node data exists in params
but how to get parent node data from child view?
Tried via context-menu:
On right click - getContextMenuItems got executed which has an input params
On this sample, child-grid doesn't have any row and node in context-params is null, it's ok,
but anyway it should be possible to retrieve the parent details at least via grid API, isn't it ?
Then I've tried to get parent via node:
but instead of detail_173 as you can see its ROOT_NODE_ID, and here is a confusion for me.
So question is that how to get parent node data (RecordID 173 just in case) through child-view context menu or any other possible way (without storing temp value, cuz multiple children's could be opened at the same time)
Yes, I've read this part Accessing Detail Grid API, and still unclear how to get parent-node via child-grid.
For React Hook users without access to lifecycle methods, use Ag-Grid Context to pass the parent node (or parent data) to the detail grid via detailGridOptions. No need to traverse the DOM or use a detailCellRenderer unless you want to :)
https://www.ag-grid.com/javascript-grid-context/
detailCellRendererParams: (masterGridParams) => ({
detailGridOptions: {
...
context: {
masterGrid: {
node: masterGridParams.node.parent,
data: masterGridParams.data
}
},
onCellClicked: detailGridParams => {
console.log(detailGridParams.context.masterGrid.node);
console.log(detailGridParams.context.masterGrid.data);
}
}
})
Able to achieve it. Have a look at the plunk I've created: Get master record from child record - Editing Cells with Master / Detail
Right click on any child grid's cell, and check the console log. You'll be able to see parent record in the log for the child record on which you've click.
The implementation is somewhat tricky. We need to traverse the DOM inside our component code. Luckily ag-grid has provided us the access of it.
Get the child grid's wrapper HTML node from the params - Here in the code, I get it as the 6th element of the gridOptionsWrapper.layoutElements array.
Get it's 3rd level parent element - which is the actual row of the parent. Get it's row-id.
Use it to get the row of the parent grid using parent grid's gridApi.
getContextMenuItems: (params): void => {
var masterId = params.node.gridOptionsWrapper.layoutElements[6]
.parentElement.parentElement.parentElement.getAttribute('row-id');
// get the parent's id
var id = masterId.replace( /^\D+/g, '');
console.log(id);
var masterRecord = this.gridApi.getRowNode(id).data;
console.log(masterRecord);
},
defaultColDef: { editable: true },
onFirstDataRendered(params) {
params.api.sizeColumnsToFit();
}
Note: Master grid's rowIds are defined with [getRowNodeId]="getRowNodeId" assuming that account is the primary key of the parent grid.
A very reliable solution is to create your own detailCellRenderer.
on the init method:
this.masterNode = params.node.parent;
when creating the detail grid:
detailGridOptions = {
...
onCellClicked: params => console.log(this.masterNode.data)
}
Here is a plunker demonstrating this:
https://next.plnkr.co/edit/8EIHpxQnlxsqe7EO
I struggled a lot in finding a solution to this problem without creating custom details renderer but I could not find any viable solution. So the real good solution is already answered. I am just trying to share another way to avoid creating custom renderer.
So What I did is that I changed the details data on the fly and added the field I required from the parent.
getDetailRowData: function (params: any) {
params?.data?.children?.forEach((child:any) => {
//You can assign any other parameter.
child.parentId= params?.data?.id;
});
params.successCallback(params?.data?.children);
}
When you expand the row to render details the method getDetailRowData gets called, so it takes in params as the current row for which we are expanding the details and the details table is set by invoking params.successCallback. So before setting the row data I am iterating and updating the parentId.

How to do single row expansion with CellTable?

I'm trying to use the new GWT CellTable widget but my table needs to support one row expansion, i.e. there is a zippy on the left of a row and when it's clicked, the row should expand to provide more detail information and this row should span across all columns. Is it possible to achieve this with the CellTable? How do I add a row that spans all columns between other rows dynamically?
Any help will be appreciated!
GWT 2.5 will add a CellTableBuilder with the exact goal of allowing this kind of things.
You can find a live example at http://showcase2.jlabanca-testing.appspot.com/#!CwCustomDataGrid (click on the "show friends" cells)
Can you not make the additional row invisible using getRowElement(int row) and using DOM methods to set display 'none' when rendered and as blank when the button, to show it, is hit.
I am working on the solution too and my plan for now is to use CSS classes + manual styles manipulation to make it look as I need. Not sure if I be able to merry it with GWT though: http://jsfiddle.net/7WFcF/
I took a different approach to solve this same problem.
The basic concept is using dom elements to add and remove rows based on an event. The following code is an abstract extension of CellTable. You'll want to call this method from your event that gets fired from the click to expand a row.
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
public abstract class ActionCellTable<T> extends CellTable<T> {
protected abstract void addActionsColumn();
Integer previousSelectedRow = null;
public void displayRowDetail(int selectedRow, Element e){
//Get the tbody of the Cell Table
//Assumption that we want the first (only?) tbody.
Element tbody = this.getElement().getElementsByTagName("tbody").getItem(0);
//Get all the trs in the body
NodeList<Element> trs = tbody.getElementsByTagName("tr");
//remove previously selected view, if there was one
if(previousSelectedRow!=null){
trs.getItem(previousSelectedRow+1).removeFromParent();
//If the current is further down the list then the current your index will be one off.
if(selectedRow>previousSelectedRow)selectedRow--;
}
if(previousSelectedRow==null || selectedRow != previousSelectedRow){// if the are equal we don't want to do anything else
Element td = Document.get().createTDElement();
td.setAttribute("colspan", Integer.toString(trs.getItem(selectedRow).getChildNodes().getLength()));
td.appendChild(e);
Element tr = Document.get().createTRElement();
tr.appendChild(td);
tbody.insertAfter(tr, trs.getItem(selectedRow));
previousSelectedRow=selectedRow;
} else {
previousSelectedRow=null;
}
}
}
previousSelectedRow is used to track which item is "expanded", this could probably be achieved using classes or IDs. If needed I can elaborate more on the CellTable, events, views, and activities.