ag-grid Refresh Column Filter after initial load - ag-grid

According to https://www.ag-grid.com/javascript-grid-filter-set/, "The grid does not update the filters for you as there are too many use cases...", #agreed.
I am using a Server Side data source with Infinite Paging querying a large set of data. Although, at initial load time, I may be confident the filter is listing all available "choices", I am hoping to find a solution to "reload" the filter at some frequency/event to be certain.
I am attempting to use the resetFilterValues() method of the object returned by a call to gridOptions.api.getFilterInstance(id).
When using a Server Side data source I am receiving the following console.error output:
ag-Grid: Set Filter cannot initialise because you are using a row model that does not contain all rows in the browser. Either use a different filter type, or configure Set Filter such that you provide it with values (Source ag-grid-enterprise.min.js:555
Note: The values method with async value load works splendidly and is written in accordance with recommendation e.g. callback to params.success with values.
I load the filter choices in the Column Header using the following approach:
{
headerName: 'Something',
field: 'SOMETHING',
width: 200,
suppressMenu: false,
suppressFilter: false,
filter: 'agSetColumnFilter',
filterParams: {
values: function (params) {
someAsyncMethodReturningAPIResultsAsArray();
}
newRowsAction: 'keep'
},
menuTabs: ['filterMenuTab']
}
I then attempt to reload the filters at a later time (like when a button is pressed outside the grid) using the following code:
var filter = gridOptions.api.getFilterInstance(id);
filter.resetFilterValues();
This code results in the error expressed above.
Q: Does anyone know how to configure Set Model to return rows as described in the error message? Is there a better way to approach this problem anyone has experience with?
Thanks

This code below can be executed in the postProcessPopup callback and it will call the values() function defined in filterParams every time the popup is opened
var filter = gridOptions.api.getFilterInstance(id);
filter.refreshFilterValues();
Note: The refreshFilterValues function is doing the trick here. It is available in v24 and above. Not too sure about older versions.

Related

How to query points with a polygon layer using Bootleaf / esri-leaflet?

I am using the Bootleaf IAG framework.
I can not figure out how to get the bounding coordinates of a filtered layer.
I am modifying the bootleaf code to query points with a polygon layer. The Query Widget already allows users to draw a polygon, but I want to select a polygon from a layer hosted on my arcgis server. I modified the filter widget by removing the text field and allowing my users to select polygon layers and values from a dropdown menu. This works fine.
Now I need to take the result of the layer.setWhere(where, handleError); code and merry it with the query below. I need selectedPolygon to equal the result of layer.setWhere(where, handleError); and use the bounding coordinates in the .within section of the query.
I have tried a number of things, L.latLngBounds, getBounds(), and toGeoJSON().features[0].geometry.coordinates to name a few, but but I can not figure out how to pull out the bounds. What is the correct code?
const query = L.esri.query({ url: pointInPolygonUrl })
.token(pointInPolygonData.token)
.within(selectedPolygon)
query.run(function (error, data, response) {
if (error) {
console.log(error);
return;
}
6/8/2021 Edit (based on Seth Lutske's comment:
I did not provide a code sandbox for two reasons: 1 - bootleaf has a lot of files, 2 - all of my layers require secure sign in to arcgis. Hopefully I can provide enough information to get assistance without it.
Is selectedPolygon changing the way I am expecting? Currently there
is no variable called selectedPolygon because I can not figure out
the correct way to format it. selectedPolygon is what I want to
call the filter result layer.setWhere(where, handleError);. I set
the polygon layer up to filter on the map as the value changes. I
can verify it is filtering as expected.
selectedPolygon format - This is where my problem lies. I can not
seem to find the correct format based on how bootleaf layers are
configured. I started with var selectedPolygon =
layer.features.geometry.coordinates; and got a geometry undefined
error. I proceeded to try every other code I could think of to get
the bounds.
Bounding coordinates may not be the proper terminology. I want to
run a query to find all of the points within the filtered polygon.
To achieve this, it is my understanding that I need to use the
bounds of the filtered polygon in the within section of the query.
6/8/2021 Edit #2
This link may be most beneficial to show how the layer is constructed. I modified this code to remove the text input and add a dropdown, but the basic definition should be the same.
Line 1605 is function addFilter()
Line 1804 is function applyFilter()
Line 1927 is layer.setWhere(where, handleFilterError);
Photo 1: console.log("polygon layer", layer)
Photo 1
Photo 2: Expanded _layers
Photo 2
Photo 3: Expanded _rings (I did not find ToGetJSON, but I found ToGeoJSON in this section.
Photo 3
It looks like if I can get to _rings then I should be fine, but that is where my knowledge is lacking.
I don't know much about bootleaf, but here are some tips to get you started. Based on your question and comments, this will hopefully clear things up and instruct you on how to apply what you need in your scenario.
Hook UI to setWhere
When the user selects an option from the UI, you can call setWhere on the layer you're providing from the arcgis server. Let's say there's a polygon layer, in my example, called statesFeatureLayer, which is an L.esri.featureLayer
// Create polygon layer of states
const statesFeatureLayer = EL.featureLayer({
url: "polygon_featurelayer_url_from_arcgis_server"
}).addTo(map);
And there's a point layer:
// Create points layer
const pointsFeatureLayer = EL.featureLayer({
url: "points_featurelayer_url"
}).addTo(map);
Now there is some UI, which has to trigger setWhere to be called on this layer. So anywhere in the UI where you want to run this functionality of setting the filter on the layer, and then querying the other layer based on the results, we'll run a function call runQuery:
function runQuery(){
statesFeatureLayer.setWhere(querystring, callback)
}
Run callback after setWhere fires
It sounds like you've already got this part figured out, and that your setWhere function is running properly. However, setWhere also takes an optional callback function as its second argument, which runs after the where has been set and the layer refreshed. Let's dig into that. In the callback, we're going to want to get all the features that are currently active on the map:
function runQuery(){
statesFeatureLayer.setWhere(querystring, () => {
statesFeatureLayer.eachActiveFeature(feature => {
// do something with features
})
})
}
Run query to test points layer against active features of polygon layer
Within eachActiveFeature, we can run a query on the pointsFeatureLayer:
function runQuery(){
statesFeatureLayer.setWhere(querystring, () => {
statesFeatureLayer.eachActiveFeature(feature => {
pointsFeatureLayer
.query()
.within(feature.toGeoJSON())
.run((error, data) => {
console.log(data);
});
})
})
}
So now were are running a query which asks for any points in the pointsFeatureLayer that are in the geometry of each active feature of the statesFeatureLayer.
The downside of this is that we can't run a query against all the active features as a group. The within query method (along with most of the other query methods) can accept singular features, whether in the form of an L.Polygon, L.Polyline, or an L.GeoJSON. While I had tried creating an L.featureGroup and calling .toGeoJSON on that, within seems to require a GeoJSON that describes only a single shape. So if you have multiple features, you'll have to conglomerate them. For example, you may have some variable results = [] at the global scope level, then within the callback of run, you can push the results to results, which will give you all results in one variable. This may take some massaging in js to get it right.
Working Codesandbox
Here you have 2 UI elements which cause runQuery to run. Either the dropdown, or the checkbox. You'll see that on every UI change, setWhere is called with a querystring constructed from the UI (setWhere for a state, and setwhere for that state and california if the checkbox is checked). When setWhere is called, its callback then runs a query against the point layer just for the currently active features, and then returns whatever points from the pointlayer are within each of the active features.

Ag-grid setFilter in server side filtering

I just want to check if it's possible to give setFilter values in callback, in form of complex objects instead of array of strings. Reason why we need setFilter to have complex objects is because we are using server side filtering and we would like to show labels in filter, but send back keys to server to perform filtering.
If we have for example objects like {name: 'some name', id: 1} we would like to show 'some name' in filter UI but when that filter is selected we need associated id (in this case 1).
By looking into source code of setFilter and corresponding model, it seems like this is not possible.
Is there a way maybe I am missing that this could work?
ag-Grid version 23.2.0
I have exactly the same problem, from the interface it seems impossible indeed because of expected string[] values
interface SetFilterValuesFuncParams {
// The function to call with the values to load into the filter once they are ready
success: (values: string[]) => void;
// The column definition object from which the set filter is invoked
colDef: ColDef;
}

ag-grid: setFilterModel() unable to apply filter to async/callback set filter

ag-grid's asynchronous set filters. These provide significant speed increases and lower transmission payloads for our clients, a very valuable feature. However, we also invoke .setFilterModel in onGridReady to load cached and saved filter configurations. These two features are unable to operate in tandem.
STEPS TO REPRODUCE Method:
Open https://embed.plnkr.co/hhgPgNM2plVpIQbB5aGj/
Select Filter icon on Set filter col column Wait for Set Filter to populate Click Apply Filter using setFilterModel() button.
Observe (function behaves as expected)
How Can setFilterModel() initiates values callback function, on success filter model is applied ? or please suggest how can I use synchronous callbacks instead of asynch issue.Thanks
I played around with the plunker and modified the applyFilter() slightly and this works.
Basically you need to notify ag-grid that you have applied the filter -
function applyFilter(){
// get instance of set filter
var valueFilterComponent = gridOptions.api.getFilterInstance('value');
// use api to select value
valueFilterComponent.selectValue('value 1');
// let ag-grid know that filter was applied
valueFilterComponent.onFilterChanged();
}
More on set filters here

How to call (filter-) valueGetter functions for external filter in ag-grid?

I am using ag-grid with angular and I want to apply an external filter to it.
The documentation shows an example what the doesExternalFilterPass function can look like:
function doesExternalFilterPass(node) {
switch (ageType) {
case 'below30': return node.data.age < 30;
...
default: return true;
}
}
It basically uses the raw data to filter the rows. As I use the columns valueGetter/filterValueGetter functions to modify the values, I want the external filter to filter based on the return values of these functions but I haven't found the proper way to do this.
I can get the value that I want by calling
this.gridOptions.api.getValue(columnId, node);
(which calls the valueGetter under the hood) instead of
node.data[columnId]
but I didn't find any example doing it that way.
So I am asking for the right way of calling the valueGetter functions inside doesExternalFilterPass or how anybody else approached this problem.
(It is especially important when you have rows that are not even included in the data object. This is the case when a columns values are calculated through a valueGetter/filterValueGetter function e.g. by adding two other columns.)
Instead of using a valueGetter, you can add a custom field to your data model that you compute before feeding data to the grid. You will need to pre-process the data and then use this new field in column definition and external filter.

how to pre-set column filter in ag-grid

I have an Ionic/Angular app using ag-grid. I would like certain grids to have a filter automatically applied when the grid is loaded - without the user having to do anything.
I tried the following:
onGridReady(params) {
params.api.sizeColumnsToFit();
// get filter instance
var filterComponent = params.api.getFilterInstance("isActive");
// OR set filter model and update
filterComponent.setModel({
type: "greaterThan",
filter: 0
});
filterComponent.onFilterChanged();
}
but it did nothing. Any ideas?
Edit: AgGrid included a onFirstDataRendered callback in version 24.0, as stated in later comments. The original answer below is now only relevant for versions which pre-date this functionality.
onFirstDataRendered(params) {
var filterComponent = params.api.getFilterInstance("isActive");
filterComponent.setModel({
type: "greaterThan",
filter: 0
});
filterComponent.onFilterChanged();
}
Reproduced your problem in a couple of their example older plunks, seemed to be alleviated by adding a small delay. Just venturing a guess that maybe the DOM isn't completely ready yet, although the grid is.
Pre-onFirstDataRendered versions:
onGridReady(params) {
params.api.sizeColumnsToFit();
setTimeout(() => {
var filterComponent = params.api.getFilterInstance("isActive");
filterComponent.setModel({
type: "greaterThan",
filter: 0
});
filterComponent.onFilterChanged();
},150)
}
I ended up doing this.
var FilterComponent = gridOptions.api.getFilterInstance('Status');
FilterComponent.selectNothing(); //Cleared all options
FilterComponent.selectValue('Approved') //added the option i wanted
FilterComponent.onFilterChanged();
I think the problem is, that the grid resets the filter when new rows are loaded. There are multiple ways to approach this:
The predefined filter types have a filter parameter called newRowsAction
https://www.ag-grid.com/javascript-grid-filter-text/#params
newRowsAction: What to do when new rows are loaded. The default is to reset the filter. If you want to keep the filter status between row loads, then set this value to 'keep'.
This answer suggests to set the gridOptions property deltaRowDataMode=true
You can also listen to one of the grid events that are emitted when the grid date changes and then apply the filter
https://www.ag-grid.com/javascript-grid-events/#miscellaneous: rowDataChanged, rowDataUpdated
These should all keep the filter when the data changes but I think you still need a bit of extra logic (setting a flag) if you want the filter only on the first load.
In my case I need to restore Ag-Grid's Set Filter when grid loads. Inspired by the accepted answer, my understanding is the filter instance api can only be accessed after grid data is ready (NOT gridReady), as it needs to aggregate row data to populate its filter list.
Therefore, as #3 adviced in #Fabian's answer, I have set up event listeners when row data changes.
You can also listen to one of the grid events that are emitted when the grid date changes and then apply the filter https://www.ag-grid.com/javascript-grid-events/#miscellaneous:
rowDataChanged, rowDataUpdated
In a similar use case I believe this is a better approach than set up an arbitrary timeout number before accessing the filter instance as it may end up with inconsistent results.
v24.0.0
The best way to implement this, is to apply your default filter in the firstDataRendered callback.
e.g.
onFirstDataRendered(params) {
//... apply your default filter here
}