AG-Grid + React - ignore grid sort when adding new row - ag-grid

In an app using AG-Grid Enterprise, React and Redux, I would like to programmatically add a new row to a grid that has sorting enabled. Is is possible to ignore grid sorting and insert this new row in a specific grid position?
To clarify this scenario, I forked the Simple Immutable Store Plnkr available at AG-Grid's website and created a new one here.
In the original Plnkr, button Append will add items to the end of the list. In the forked version, due to column Price ascending sort and due to the fact that new items do not have a price defined anymore, all appended items will show up in the first positions of the grid.
My understanding is that this happens because in the last line from the snippet below (this.gridApi.setRowData), AG-Grid will add new rows and trigger sort, filters, etc.
addFiveItems(append) {
var newStore = immutableStore.slice();
for (var i = 0; i < 5; i++) {
var newItem = createItem(append);
if (append) {
newStore.push(newItem);
} else {
newStore.splice(0, 0, newItem);
}
}
immutableStore = newStore;
this.gridApi.setRowData(immutableStore);
}
With that in mind, is there any AG-Grid API that will allow items to be added in specific grid positions while sort is activated? The goal is to keep Append button original behaviour: append items to the end of the grid, even if sort is defined in this AG-Grid Enterprise, React and Redux scenario.

Turns out that from AG-Grid 17+, a new callback, called postSort is available (see https://www.ag-grid.com/javascript-grid-sorting/#post-sort).
As detailed in the documentation above, this callback may be used to tweak row order even after sort is applied.

Related

What is the best way to handle deleting rows in Ag-Grid when using Redux?

Adding rows is pretty straightforward - just have to add a new row in my Redux store, which then updates my table.
However if I delete a row from my Redux store the row data still seems to hang around somewhere - I have a few custom cell renderers which will then trigger errors as the data gets passed to them from Ag-Grid, but the row no longer exists in my store creating the errors.
Code example would be helpful here to figure out the source of issue- in meantime please check if after manipulating your store, you do refresh the grid data - something like:
gridOptions.api.setRowData(gridOptions.rowData)
More about ag-grid row data updating can be found here:
https://www.ag-grid.com/javascript-grid-data-update/
https://www.ag-grid.com/react-redux-integration-pt1/
Data binding in React can be achieved by using a controlled input. A controlled input is achieved by binding the value to a state variable and a onChange event to change the state as the input value changes.
See the below snippet:
onRemoveSelected() {
var selectedData = this.gridApi.getSelectedRows();
var res = this.gridApi.updateRowData({ remove: selectedData });
this.props.actions.delete(selectedData.id);
}
Unfortunately neither of these suggestions worked in my case.
Setting a debugger in my custom cellRenderer showed that the row had been removed from this.props.api.gridOptionsWrapper.gridOptions.rowData, yet the cellRenderer was still called for the deleted row.
The quick fix solution I came up with was just to test if the row (this.props.data) using the cellRenderer existed in this.props.api.gridOptionsWrapper.gridOptions.rowData in a shouldComponentUpdate within the cellRenderer.
shouldComponentUpdate(nextProps) {
const { api, data } = nextProps;
const gridRowDataIds = api.gridOptionsWrapper.gridOptions.rowData.map(
row => {
return JSON.parse(row.rowMetadata).id;
}
);
const dataPropsId = JSON.parse(data.rowMetadata).id;
return gridRowDataIds.includes(dataPropsId);
}
Note: rowMetadata is just a hidden column used to hold various properties regarding the row, such as an id, visibility, if it has been edited, etc.

Is there any way/api to set a certain row to be shown at the top in ag-grid?

When I don't do/call any grid api, it, of course, displays the grid-look with the very first index row. But here what I'd like to achieve is with ag-grid is that I want to set a certain row to be shown at the top. I've tried using ensureIndexVisible api with targetIndex on the firstDataRendered event hook but it causes an awkward user experience as it scrolls down to the row after it finishes rendering. So I'm wondering if there's any other approach for this needs yet with un-awkward user experience. Here's my trying snippet:
Using Angular 7
// template
<ag-grid-angular
[rowData]="rowData"
(firstDataRendered)="onRendered()">
</ag-grid-angular>
// component
onRendered(): void {
const targetIndex = this.getTargetIndex();
this.gridOptions.api.ensureIndexVisible(targetIndex, 'top');
}
You can use Row Pinning feature of ag-grid to pin single or multiple rows to the top or bottom of the grid always.
The api to set pinned rows is setPinnedTopRowData.
this.gridApi.setPinnedTopRowData(rows);
Sample plunker which demonstrates the row pinning feature - https://plnkr.co/edit/?p=preview
Read more about row pinning in the ag-grid documentation here - https://www.ag-grid.com/javascript-grid-row-pinning/

ag-grid: Using Drag drop to reorder rows within the grid

I need to re-order rows within ag-grid by using drag-drop. The drag drop methods are simple enough but when the drop occurs there seems no easy way to switch the rows locations.
I attempted a
gridOptions.api.removeItems(selectedNode);
to clear the current and then
gridOptions.api.insertItemsAtIndex(2, savedNode);
but the drop event appeared to re-fire preventing that approach. Plus the insertItems (when ran first) falls over in internal ag-grid looping.
I would rather not re-sort the gridRows manually and then reset the gridRow data which would be somewhat clunky. This seems a common request on most grids so i assume it can be done but have just missed the relevant documentation. Thanks for any help..
K have finally got an Angular 2 method working, though currently I have to switch off sorting and filtering on the grid.
This particular example is relying on row selection being enabled (due to how it is finding some records). Grid dragdrop should be disabled (as that drags the grid visually which sucks) Instead use a processRowPostCreate to set up draggable params at the row level. This sets the dragged option to the row which looks much nicer.
in gridOptions
processRowPostCreate: (params) => {
this.generateRowEvents(params);
},
which calls
private generateRowEvents(params) {
params.eRow.draggable = true;
params.eRow.ondragstart = this.onDrag(event);
params.eRow.ondragover = this.onDragOver(event);
params.eRow.ondrop = this.onDrop(event);
}
I track the source recrord in the onDrag method
var targetRowId: any = $event.target.attributes.row.value;
this.savedDragSourceData = targetRowId;
onDragOver as usual
On drop we have to protect against an infinite loop (ag-grid appears to recall live methods when items are added to the grid hence the ondrop occurs multiple times) and then insert, delete and splice over both the grid and its data source ( I will carry on looking at using the grid to populate the data as opposed to the source data as that would allow source/filter, currently doign that inserts blank rows). An event (in this case) is then emitted to ask the owning component to 'save' the adjusted data.
private onDrop($event) {
if ($event && !this.infiniteLoopCheck) {
if ($event.dataTransfer) {
if (this.enableInternalDragDrop) {
this.infiniteLoopCheck= true;
var draggedRows: any = this.gridRows[this.savedDragSourceData];
// get the destination row
var targetRowId: any = $event.target.offsetParent.attributes.row.value;
// remove from the current location
this.gridOptions.api.removeItems(this.gridOptions.api.getSelectedNodes());
// remove from source Data
this.gridRows.splice(this.savedDragSourceData, 1);
if (draggedRows) {
// insert into specified drop location
this.gridOptions.api.insertItemsAtIndex(targetRowId, [draggedRows]);
// re-add rows to source data..
this.gridRows.splice(targetRowId, 0, checkAdd);
this.saveRequestEvent.emit(this.gridRows);// shout out that a save is needed }
this.v= false;
}
else {
this.onDropEvent.emit($event);
}
}
}
}
NOTE we are wrapping the grid in our own class to allow us to write one set of grid methods and not replicate over the application whenever the grid is used.
I will be refining this over the next few days but as a basic this allows the ag-grid in angular 2 to drag drop rows to sort records.
If you don't find a solution within ag-grid then you can do this by adding one more directive("ngDraggable") and integrate it with ag-grid.
Please find the following working plnkr for this.
https://embed.plnkr.co/qLy0EX/
ngDraggable
Hope this helps..

How to set css class to a row which has focus in Dojo datagrid

I am using DOJO datagrid version 1.10 What I want is on tab indexing the row in the grid should get highlighted so that user will be able to know on which row the focus is. But I am not getting the row focus.
You could listen on the dojox.grid.DataGrid::onCellFocus event. The event arguments are the focused cell-Object itself and the corresponding rowIndex.
function onCellFocus(cell, rowIndex) {
// first clear selection
grid.selection.clear();
// select the focused row
grid.selection.setSelected(rowIndex, true);
// invoke manually the render method
grid.render();
}
I've created a working fiddle for you, which can be found here.

how to update model for one combobox based on selection of another combobox?

I have two sap.m.ComboBox in a table row. Items in second ComboBox varies on selction of first ComboBox. Following is the code i have written but its not refreshing the items in second ComboBox.
var firstCombo = new sap.m.ComboBox({items:[new sap.ui.core.Item({text:"EmployeeLevel4"}),
new sap.ui.core.Item({text:"EmployeeLevel3"})],
selectionChange : oController.comboBoxMapping});
var secondCombo = new sap.m.ComboBox("SecondComboBox",{});
In Controller
comboBoxMapping:function(oEvent){
var filterMappingModel = \\getting the model;
sap.ui.getCore().byId("SecondComboBox").setModel(filterMappingModel);
sap.ui.getCore().byId("SecondComboBox").bindItems('/', new sap.ui.core.Item({key: '{Skill}',text: '{Skill}'}),true);
}
Suggest me some options to impelemet it correctly.
In the selectionChange event handler of the first combo, you should set the filter of the ContextBinding of the items of the second combo. E.g.:
this.getView().byId("secondComboBox").getBinding("items").filter([
new sap.ui.model.Filter(
"CategoryID",
"EQ",
oEvent.getParameter("selectedItem").getKey())
]);
To see this in action, have a look at this jsbin.
As mentioned in on of the responses to this answer, the examples above show only how to change the binding of a combobox. Since your comboboxes are embedded in a table you will first have to figure out which of the comboboxes needs to be changed as there are several in the table.
It would be my advice so look at oEvent.getSource(), which should result in the combo that has just been changed. From there, you could use getParent() to go up in the control hierarchy until you've reached the row. Within that row, you should have several columns, and one of those columns contains the SecondCombo box. If you know which row contains your SecondCombo, you will be able to get it using byId and set the filter as mentioned earlier.