senyamur / ag-grid-odata Goto Github PK
View Code? Open in Web Editor NEWImplementation Odata ServerSideDatasource for ag-grid
License: MIT License
Implementation Odata ServerSideDatasource for ag-grid
License: MIT License
Hello Semen,
I've just noticed one more critical thing that stopped to work in our project after upgrade of ag-grid.
It is filtering. No calls to API when change filtering settings.
Here is fork for you with customer field enabled to filter:
https://codesandbox.io/s/ag-grid-server-side-row-model-sample-forked-b0p8b
With ag-grid@25 and 1.0.20/1.0.19 ag-grid-odata open Filters tab and try to filter Customers. No any call to API.
It to switch to ag-grid@24 -- all works - request to API is performed.
It would be great if you find time to fix this.
Thanks in advance.
Steps to Reproduce:
Create a dataset with date fields.
Configure Ag-Grid to display the dataset, allowing grouping by date fields.
Attempt to group the data by date.
Group works as expected.
Expand grouped row.
Odata uses date to filter value as string (not a date type)
Expected Behavior:
Odata filter uses date as intended.
https://odatasampletest.azurewebsites.net/odata/Heroes?$count=true&$top=100&$filter=AddedOn eq 2024-03-28
Actual Behavior:
When expand a grouped column (by date), Odata sends a request as grouped Date is a type of string. this triggers a backend error.
https://odatasampletest.azurewebsites.net/odata/Heroes?$count=true&$top=100&$filter=AddedOn eq '2024-03-28'
I have attached a repo. for the following issue.
Steps:
AddedOn
columnSo I hit another "issue" or what to call it with the plugin and how it creates the ODATA filter string.
I do realize that perhaps this issue could be solved by configuring the backend, but I'm raising the issue anyway to perhaps add more flexibility to the package somewhere down the line.
Another thing that would add more flexibility would be the possibility to on a per-column setting set if it should be case sensitive or not. Ag-Grid has this setting for text (https://www.ag-grid.com/react-grid/filter-text/) and the caseSensitive
setting.
For regular text filters that searches text strings in the ODATA it works just fine with isCaseSensitiveStringFilter
set to false (or omitted). However for agSetColumnFilter that ODATA filters on Enums it does not.
Setting isCaseSensitiveStringFilter
to true will make agSetColumnFilter work as expected, but will on the other hand force case sensitivity on fields that are simple text searches.
An example:
Column A contains colors as strings (White, Black, Green etc.) that I can search for using the regular agTextColumnFilter. It will by default return all matches if i type "gre" and will be case insensitive as well. All is good.
Column B however contains Color Code enums instead. Lets say 1000 = White, 2000 = Black, 3000 = Green and will display the codes (1000, 2000, 3000) and that is also what I filter on. The column is set to be a agSetColumnFilter with the values 1000, 2000, 3000 as possible filter values.
If I set isCaseSensitiveStringFilter: true
it works and the ODATA query looks like this:
[odata-address]?$count=true&$top=250&$filter=ColorCode%20in%20(%271000%27)
and returns rows as expected.
If I set it to false the request looks like this:
[odata-address]?$count=true&$top=250&$filter=tolower(ColorCode)%20in%20(%271900%27)
And the ODATA address returns 400 (bad request)
{
"error": {
"code": "",
"message": "The query specified in the URI is not valid. No function signature for the function with name 'tolower' matches the specified arguments. The function signatures considered are: tolower(Edm.String Nullable=true).",
"details": [],
"innererror": {
"message": "No function signature for the function with name 'tolower' matches the specified arguments. The function signatures considered are: tolower(Edm.String Nullable=true).",
"type": "Microsoft.OData.ODataException",
"stacktrace": "
at Microsoft.OData.UriParser.FunctionCallBinder.MatchSignatureToUriFunction(String functionCallToken, SingleValueNode[] argumentNodes, IList`1 nameSignatures)\n
at Microsoft.OData.UriParser.FunctionCallBinder.BindAsUriFunction(FunctionCallToken functionCallToken, List`1 argumentNodes)\n
at Microsoft.OData.UriParser.FunctionCallBinder.BindFunctionCall(FunctionCallToken functionCallToken)\n
at Microsoft.OData.UriParser.MetadataBinder.Bind(QueryToken token)\n
at Microsoft.OData.UriParser.MetadataBinder.<BindIn>b__22_0(QueryToken queryToken)\n
at Microsoft.OData.UriParser.InBinder.GetSingleValueOperandFromToken(QueryToken queryToken)\n
at Microsoft.OData.UriParser.InBinder.BindInOperator(InToken inToken, BindingState state)\n
at Microsoft.OData.UriParser.MetadataBinder.BindIn(InToken inToken)\n
at Microsoft.OData.UriParser.MetadataBinder.Bind(QueryToken token)\n
at Microsoft.OData.UriParser.FilterBinder.BindFilter(QueryToken filter)\n
at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseFilter()\n
at Microsoft.AspNetCore.OData.Query.FilterQueryOption.get_FilterClause()\n
at Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.Validate(FilterQueryOption filterQueryOption, ODataValidationSettings settings)\n
at Microsoft.AspNetCore.OData.Query.Validator.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\n
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)\n
at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuting(ActionExecutingContext actionExecutingContext)"
}
}
}
It serialize to odata like "1995-10-08 00:00:00T00:00:00.000Z", it is wrong format.
#8 - I couldn't reopen, sorry for new.
Not in all places - https://github.com/SenyaMur/ag-grid-odata/blob/master/src/OdataProvider.ts#L669
And it is my case!
Hi! @SenyaMur
Thank you for this great little package! It seems to work like magic in combination with ASP.NET and Entity Framework.
I found a bug though. You can quickly repro it with the demo by adding filter="agNumberColumnFilter"
to Price and filtering it with AND/OR (for example less than 200 and greater than 50).
The error is:
TypeError
me.odataOperator[col.type] is not a function
and also:
ReferenceError
keyEventHandler is not defined
Hi there is this error
ERROR TypeError: Cannot read properties of undefined (reading 'setSecondaryColumns') at OdataProvider.getRows (OdataProvider.js:720)
Angular Version 13
ag-grid version is 26.2.0
Thanks
Hi friend
can I run OData on angular 8 with ag-grid?
Hello again,
So I've hit another issue that I'd like to discuss.
The ODATA query URL then looks like this:
$filter=tolower(ColumnName)%20in%20(%27c%27,%27d%27,%27e%27,%27k%27)
(unencoded: $filter=tolower(ColumnName) in ('c','d','e','k')
)
and results are returned as expected.
Using the (Blanks) value however, where the value actually is NULL will give 0 results. This is because the query looks like this:
$filter=tolower(GearboxCode)%20in%20(%27null%27)
(unencoded: $filter=tolower(GearboxCode) in ('null')
)
but would need to look like this:
$filter=tolower(GearboxCode)%20in%20(null)
(unencoded: $filter=tolower(GearboxCode) in (null)
)
(note the lack of '' [encoded as %27] around the null value).
Is there an easy way to solve this and have the query work as I'd need above?
Hello my friend
There is a dependency problem in your package, please check and fix this
Could not resolve dependency:
npm ERR! peer ag-grid-community@"^26.2.1" from [email protected]
npm ERR! node_modules/ag-grid-odata
npm ERR! ag-grid-odata@"^1.0.29" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/ag-grid-community
npm ERR! peer ag-grid-community@"^26.2.1" from [email protected]
npm ERR! node_modules/ag-grid-odata
npm ERR! ag-grid-odata@"^1.0.29" from the root project
Hello again!
I have expanded my implementation of the ag-grid-odata package in my solution.
I do the following:
const onGridReady = async (event: GridReadyEvent) => {
event.api.setServerSideDatasource(
new OdataProvider({
callApi: (options) => fetch(`[backendAddress]${options}`, {
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
})
.then((resp) => resp.json())
.then((resp) => {
// other code removed for brevity
return resp;
}),
beforeRequest: (query, provider, request) => {
query.filter = createExpandedOdataQuery();
console.log(query);
},
isCaseSensitiveStringFilter: false,
caseSensitiveColumns: viewSetting ? viewSetting.enumColumns : undefined,
}) as unknown as IServerSideDatasource,
);
};
and everything works as expected for the built in filters (and works great with your update the other day as well with caseSensitiveColumns).
However I do run a function on beforeRequest
as you can see, createExpandedOdataQuery()
. This function returns a string array with extra odata filters built from an external input field that I attempt to use a more free form text search field. (I take the input string and creates a supplementary ODATA string out of it).
However it does not seem to actually use any state strings or updated variants of these.
Just to make a test I did the following:
I setup the provider as above, in beforeRequest I simply added a console.log() for a state (const [test, setTest] = React.useState<string>();
)
and then whenever I recieve the state that a search should be executed I set the test-state to be a different string, but that is never picked up. If I check the state of test it is indeed updated. And it does not seem to be an async issue either, because subsequent searches still does not log the updated string from the test state.
create state, set it to "foo" as default, then set it to "bar". It is indeed logged with its updated value.
const [test, setTest] = React.useState<string>('foo');
React.useEffect(() => {
if (search.performSearch) {
setTest('bar');
}
}, [search.performSearch]);
console.log(test); // returns "foo" on first render, returns "bar" efter recieving search.performSearch)
But in here its always logged as "foo" no matter how many times I execute things.
beforeRequest: (query, provider, request) => {
console.log(test);
},
Is this by design or am I missing something?
I did some experiments with setting the serverSideDataSource in React.useEffect, but then it will execute two serverSideDataSource requests, one that was registered already and then the new one that is added in my useEffect :(
Edit:
Expanding a bit on the above.
I have tried the following as well, making sure that the state is set before I trigger a onFilterChanged in the useEffect. The flow works as expected with were it triggers the onFilterChanged etc. beforeRequest still does not fetches the updated value from the string though :(
const [newString, setNewString] = React.useState<Array<string>>();
React.useEffect(() => {
const createExpandedOdataQuery = () => {
// code for creating ODATA query
return odataQueryString !== '' ? [`${odataQueryString}`] : undefined;
};
if (search.performSearch) {
setNewString(createExpandedOdataQuery());
}
}, [agGridApi.gridApi, search.performSearch, search.searchString]);
React.useEffect(() => {
if (newString) {
agGridApi.gridApi?.onFilterChanged();
}
}, [agGridApi.gridApi, newString]);
const onGridReady = async (event: GridReadyEvent) => {
event.api.setServerSideDatasource(
new OdataProvider({
...,
beforeRequest: (query, provider, request) => {
console.log(newString);
query.filter = newString;
},
isCaseSensitiveStringFilter: false,
caseSensitiveColumns: viewSetting ? viewSetting.enumColumns : undefined,
}) as unknown as IServerSideDatasource,
);
};
Is it possible the npm package is referencing older version of ag-grid?
I'm finding some errors, I think maybe ag-grid changed how to reference it.
e.g.
Error: node_modules/ag-grid-odata/build/OdataProvider.d.ts:2:10 - error TS2305: Module '"@ag-grid-enterprise/all-modules"' has no exported member 'IServerSideGetRowsParams'.
and for example, in the types.d.ts this does not compile:
import { ColumnVO } from '@ag-grid-community/all-modules';
but this is ok:
import { ColumnVO } from 'ag-grid-community';
The issue is related to object we work with if we receive more rows than we expect to get:
Lines 497 to 500 in fb165f9
To fix it, the code in line 498 should be the following:
count_1 = y.value[0].count;
Please can you check the issue on your side and fix it.
Regards and thank you.
if select date from ag grid picker(browser picker), this library adds orphan time
AR: $filter: StartDate eq 2020-07-15 00:00:00T00:00:00.000Z
ER: $filter: StartDate eq 2020-07-15T00:00:00.000Z
index.es.js=>184 Line of Code. Can it somehow configured? No special settings of Ag Grid
My case is:
I did workaround
new OdataProvider({
callApi: options => {
if (!options.includes('$count=')) options = options + "&$count=true"
...
But I think it is not good.
When I try to use column with name like 'Requirement.Customer.Name' with expansion depth bigger then 2 it is not working.
I think it is because of you use js function replace (single replace only) instead of replaceAll when you change "." to "/"
Could you repair it?
There is no parameter to sort the grouped columns.
I am using the Server Side mode.
Steps to replicate:
Expected Result: get?$top=100&$apply=groupby((Column_Name)),aggregate($count%20as%20childCount))&$orderBy=childCount
Actual Result: get?$top=100&$apply=groupby((Column_Name)),aggregate($count%20as%20childCount))
Hi my friend
In the new ag-grid update, a number of methods have been renamed. Thank you for checking.
WARNING:
=> AG Grid: since version 28.0.x setSecondaryColumns has been renamed, please use setPivotResultColumns instead
The demo linked in the description doesn't work anymore: https://codesandbox.io/s/ag-grid-server-side-row-model-sample-zqujt?fontsize=14&hidenavigation=1&theme=dark
It references this Heroku app that has since been removed or disabled: https://odatav4sample.herokuapp.com/odata/$metadata
On run storybook receive error:
ERROR in ./node_modules/ag-grid-odata/build/index.js 1:9
Module parse failed: Unexpected token (1:9)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> export * as types from './types';
| export { OdataProvider as default, OdataServerSideProvider, OdataProviderClient } from './OdataProvider';
| //# sourceMappingURL=index.js.map
......
Demo link seems to crash transpiling.
Also, any chance there is an angular sample?
Hi,
While looking at the code I took some notes that I thought I'd add as proposed improvements. They are not at all related to functionality (still working great!), but rather to ease development and reading for users of the package.
I might do a PR if I have the time and get around to it. :)
Edit: I forked and created a branch here: https://github.com/kosmiq/ag-grid-odata/tree/split-into-modules. It's just a proposal, so I'll let you decide what to do with it. :)
First off, I can not thank you enough for creating and publishing this package. It just saved me a bunch of hours and implementation and testing. It seems to work really well, and was easy to implement and manages filter in a way that I do really appreciate and like as a baseline.
We are in a TypeScript (React) project, and using the examples as they are produces typescript errors:
I did not really look into the types, but it is solvable by doing typecasting as below:
Preferably the OdataProvider
would export itself with a type corresponding to what is wanted by setServerSideDatasource
in a typescript setup.
This is mostly to raise awareness of the issue and potentially help others that might hit the same snag.
Hi SenyaMur,
I've been using your OData plugin for ag-grid for a small 4 row table, which is great . I am having an issue with server side pagination (aka, loads each page from an api call when clicking on a new page) and was wondering what I'm doing incorrectly. Given below...
onGridReady(params: GridReadyEvent) {
params.api.setServerSideDatasource(
new OdataServerSideProvider({
callApi: (options) =>
axiosApp
.get(/odata/v1/Data${options}
)
.then((resp) => {
return resp.data;
}),
beforeRequest: (
query: OdataQueryOptions,
provider: OdataProvider,
request: IGetRowsParams | IServerSideGetRowsRequest
) => {
query.select = new Array();
this.state.columnDefs.forEach((column, index) => {
query.select?.push(column.field);
});
// NOTE: I do this to get any pagination going otherwise all the items are returned
request.startRow = params.api.paginationGetCurrentPage();
request.endRow = params.api.paginationGetPageSize();
},
afterLoadData: (options, rowData, totalCount) => { // NOTE: totalCount is correct at 6
if (options.skip === 0 && rowData.length > 0) {
params.api.sizeColumnsToFit();
}
},
})
);
}
I get below in return which is correct from the backend but the total count for the pagination is 4 in the table and I can't get to the next two items. In afterLoadData totalCount is correct at 6. Also (see NOTE) the only way I can get pagination in the url is to do the below by hand. Does the plugin support server side pagination (if I return everything the pagination is fine)?
// See Note: above
request.startRow = params.api.paginationGetCurrentPage();
request.endRow = params.api.paginationGetPageSize();
{
"@odata.context": "https://localhost:5001/odata/v1/$metadata#Data(Name,Description,TimeFrame,Id)",
"@odata.count": 6,
"value": [
{
"Name": "Name1",
},
{
"Name": "Name2",
},
{
"Name": "Name3",
},
{
"Name": "Name4",
}
]
}
Hello Semen,
First of all - thank you for this library! Seems that it is the only one odata client for ag-grid.
Recently after upgrade ag-grid to the latest version I faced to the problem in pivot mode. Here is a fork:
https://codesandbox.io/s/ag-grid-server-side-row-model-sample-forked-q9z15
Steps to reproduce:
The error appears:
Cannot read property 'blocks' of undefined
OdataProvider.eval
https://q9z15.csb.app/node_modules/ag-grid-odata/build/index.es.js:529:83
step
https://q9z15.csb.app/node_modules/ag-grid-odata/build/index.es.js:110:17
Could you please take a look?
To replicate use the Server-Side model
Steps to replicate:
Expected result:
The data is sorted, sorting of groups remains the same.
Actual Result:
Group is collapsed, group column is not sorted.
Applicable to the case when 2 or more groups are opened.
There is no clue for handling pagination in ag-grid on serverside. When the row data is less than the expected paginationPageSize, the next button should be disabled because there is no more data.
For example the paginationPageSize = 10 and the rowData.length = 3. The pagination should be stopped.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.