AG-GRID: Sort the Current Page Rows in Infinite Row Model - ag-grid

I am new to ag-grid and JS and I am trying to create a Server Side Paginated View of my data.
These are the Test settings I am currently using. They are working fine.
datasource: updateDataSource(),
rowModelType: 'infinite',
cacheBlockSize: 5,
rowBuffer: 0,
paginationPageSize: 5,
cacheOverflowSize: 2,
maxConcurrentDatasourceRequests: 1,
maxBlocksInCache: 5,
infiniteInitialRowCount: 5,
pagination: true,
paginationAutoPageSize: false
But now I want to implement sort functionality. Through the documentation I found we can pass the sortModel to the Server and get the sorted data, but I don't want to sort the data on the Server Side. Is there any way to write a sorting logic for every column on the Client Side itself ?
My getRows Method,
getRows: (params) => {
currentPage = corporateActionGridOpts.api.paginationGetCurrentPage();
let sortModel = params.sortModel[0];
data = fetchData(currentPage);
data.then((res) => {
let totalRows = res.headers["x-total-count"];
if(sortModel) {
res = sortData(res, sortModel)
}
params.successCallback(res, totalRows);
})
}
};
Currently I am using this logic to sort the columns on the Client Side,
function sortData(data, sortModel) {
let comparator;
switch(sortModel.colId) {
case 'id':
switch(sortModel.sort) {
case 'asc':
comparator = function compare(a, b) {
if ( a.id < b.id ){
return -1;
}
if ( a.id > b.id ){
return 1;
}
return 0;
}
break;
case 'desc':
comparator = function compare(a, b) {
if ( a.id > b.id ){
return -1;
}
if ( a.id < b.id ){
return 1;
}
return 0;
}
break;
}
break;
}
data.sort(comparator);
return data;
}
But writing this for all the columns is too much code. Is there any better way to do this ? I have about 15 columns total.

Related

How do I iterate through selected cells in AgGrid?

I would like to delete the values in all selected cells when the user presses backspace or delete, how do I iterate through the selected cells?
I'm using React and Typescript
The docs are out of date, this worked for me, in react / typescript
(
// The type of this is shown below, but is impossible to work with
event: any
// | CellKeyPressEvent<CountryLineProductRow>
// | FullWidthCellKeyPressEvent<CountryLineProductRow>
) => {
if (event.event.key === 'Backspace' || event.event.key === 'Delete') {
event.api.getCellRanges().forEach((cellRange: CellRange) => {
if (cellRange.startRow && cellRange.endRow) {
const startRow = Math.min(
cellRange.endRow.rowIndex,
cellRange.startRow.rowIndex
);
const endRow = Math.min(
cellRange.endRow.rowIndex,
cellRange.startRow.rowIndex
);
cellRange.columns.forEach((column: Column) => {
if (column.getColDef().editable) {
for (
let rowNumber = startRow;
rowNumber <= endRow;
rowNumber++
) {
const model = event.api.getModel();
const rowNode = model.rowsToDisplay[rowNumber];
rowNode.setDataValue(column.getColDef().field, null);
}
}
});
}
});
}
}

w2ui filter option "contains not" possible?

I am using w2ui (1.5) and I would be interested in whether it is possible to use a filter that only delivers negative results
That means only records/keys which not fullfilling a certain criteria.
Like a condition "contains not" or "is not" in addition to
http://w2ui.com/web/docs/1.5/w2grid.textSearch.
Thanks!
Gordon
okay, a possible solution is
w2ui['grid'].search([{ field: var1, value: var2, operator: 'not in'}], 'OR');
I coded my own solution to this problem. This adds a way to use "not" for string and "!=" for number searches.
This function does the search and it is also used to store the grid advanced search popup in history state.
I'm sure this can be even more optimized, so please use this more like a guideline. Hope this helps somebody.
function searchExtend(event, grid) {
// if event is null, we do just the local search
var searchObj;
if (event == null) {
searchObj = grid;
} else {
searchObj = event;
}
// make a copy of old data
const oldSearchData = structuredClone(searchObj.searchData);
const oldSearchLogic = structuredClone(searchObj.searchLogic);
var searchData = searchObj.searchData;
var invertedSdata = [];
var toSplice = [];
// check operator if it's "not" or "!="
for (var i = 0; i < searchData.length; i++) {
var sdata = searchData[i];
// invert the condition
if (sdata.operator == "not") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "contains",
value: sdata.value
});
}
if (sdata.operator == "!=") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "=",
value: sdata.value
});
}
}
// remove all "not" and "!=" from searchData
for (var i in toSplice) {
searchData.splice(i, 1);
}
var foundIds = [];
// use inverted criteria to search
if (invertedSdata.length > 0) {
grid.searchData = invertedSdata;
grid.searchLogic = "OR";
grid.localSearch();
grid.searchLogic = oldSearchLogic;
// store found ids
foundIds = structuredClone(grid.last.searchIds);
}
if (foundIds.length > 0) {
// perform a search with original criteria - spliced "not" and "!="
grid.searchData = searchData;
grid.localSearch();
var allRecIds = structuredClone(grid.last.searchIds);
// if there's not any results, push push all recIds
if (grid.last.searchIds.length == 0) {
for (let i = 0; i < grid.records.length; i++) {
allRecIds.push(i);
}
}
// remove all ids found with inverted criteria from results. This way we do the "not" search
for (const id of foundIds) {
allRecIds.splice(allRecIds.indexOf(id), 1);
}
if (event != null) {
// let the search finish, then refresh grid
event.onComplete = function() {
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
} else {
// refresh the grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
return;
}
if (event != null) {
event.onComplete = function() {
setSearchState(grid); // store state
}
} else {
// refresh whole grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
}
function refreshGrid(grid, allRecIds, oldSearchData) {
grid.last.searchIds = allRecIds;
grid.total = grid.last.searchIds.length;
grid.searchData = oldSearchData;
grid.refresh();
}
function setSearchState(grid) {
history.replaceState(JSON.stringify(grid.searchData), "Search");
}
To use this, you have to call it from the grid's onSearch:
onSearch: function(event) {
searchExtend(event, w2ui["grid"]);
}
Also, if you want to use the history.state feature, it needs to be called from onLoad function:
onLoad: function(event) {
event.onComplete = function() {
console.log("History state: " + history.state);
if (history.state != null) {
w2ui["grid"].searchData = JSON.parse(history.state);
searchExtend(null, w2ui["grid"]);
}
}
To add operators, please use this reference.
This is my solution to the problem:
operators: {
'text': ['is', 'begins', 'contains', 'ends', 'not'], // could have "in" and "not in"
'number': ['=', 'between', '>', '<', '>=', '<=', '!='],
'date': ['is', 'between', {
oper: 'less',
text: 'before'
}, {
oper: 'more',
text: 'after'
}],
'list': ['is'],
'hex': ['is', 'between'],
'color': ['is', 'begins', 'contains', 'ends'],
'enum': ['in', 'not in']
}

Svelte doesn't reorder Meteor collection

My Svelte component is set up to find all people in a MongoDB document and list them in a table. When clicking on a column header the collection should sort by that column/field and it should toggle the sort direction with subsequent clicks.
My script section:
$: sortColumn = 'name';
$: sortDirection = 'asc';
$: sortParameters = setSortParams(sortColumn, sortDirection);
$: contactList = [];
function getContactList(sortObj) {
contactList = Contacts.find({
isBlocked: false,
isDeleted: { $ne: true }
},
{
sort: sortObj
}).fetch();
contactList = contactList;
}
onMount(() => {
getContactList(setSortParams(sortColumn, sortDirection));
});
function changeSortDirection() {
if (sortDirection === 'asc') {
sortDirection = 'desc';
} else {
sortDirection = 'asc';
}
}
function sortByColumn(col) {
if (col === sortColumn) {
changeSortDirection();
} else {
sortDirection = 'asc';
}
sortColumn = col;
getContactList(setSortParams(sortColumn, sortDirection));
}
function setSortParams(sortField, sDirection) {
let sortParams = [];
let direction = sDirection || 1;
let field = sortField || 'name';
if (direction === 'asc') {
direction = 1;
} else {
direction = -1;
}
if (field === 'name') {
sortParams.push(['firstName', direction]);
sortParams.push(['lastName', direction]);
} else {
sortParams.push([field, direction]);
}
sortParams = sortParams;
return sortParams;
}
And the relevant part of my svelte file:
{#each columns as column}
<th class="contact-table__column contact-table__column-header"
on:click={() => sortByColumn(column.type)}>
<span class="contact-table__title">{column.display} {sortDirection}</span>
</th>
{/each}
The collection reorders when I click on a different column header, but it doesn't reorder when I click on the same header (it should switch between ASC and DESC sort order).
I'm new to Svelte and Meteor so I'm sure there's a few things I'm doing wrong. I appreciate any help.

Some part of your SQL statement is nested too deeply

I have the following code
[WebGet]
public Bid GetHighestBidInOpenAuctions(int auctionEventId)
{
var auctionEvent = CurrentDataSource.AuctionEvents.Where(x => x.Id == auctionEventId).FirstOrDefault();
var auctionIds = CurrentDataSource.Auctions.Where(x => x.AuctionEventId == auctionEventId && x.Ends > DateTime.UtcNow).Select(x => x.Id).ToList();
var bids = CurrentDataSource.Bids.Where(x => auctionIds.Any(t => t == x.AuctionId));
// If the auction Event has not yet started or there are no bids then show auction with high pre-sale estimate.
if (bids.Count() == 0 || auctionEvent.Starts > DateTime.UtcNow)
{
return null;
}
var highestBid = bids.Where(b => b.IsAutobid == false).OrderByDescending(b => b.Amount).FirstOrDefault();
return highestBid;
}
This line throws the below exception
if (bids.Count() == 0 || auctionEvent.Starts > DateTime.UtcNow)
Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries.
What's wrong?
EDIT
I have tried doing this
IQueryable<Bid> bids = CurrentDataSource.Bids.Where(b => 0 == 1);
foreach(var auctionId in auctionIds)
{
int id = auctionId;
bids = bids.Union(CurrentDataSource.Bids.Where(b => b.AuctionId == id));
}
But I still get the same error.
Rather than using a subquery, try replacing the bid query with:
var bids = CurrentDataSource.Bids.Where(b => b.AuctionEventId == auctionEventId
&& b.Auction.AuctionEvent.Starts > DateTime.UtcNow
&& b.Auction.Ends > DateTime.UtcNow);
if (bids.Count() == 0
{
return null;
}
It seems when you have too many things in your database then you will get this error (auctionIds in my case) because the generated sql will be too deeply nested. To solve this I came up with this solution. If anyone can do better then do. I'm posting this because someone may have this error in the future and if they do, in the absence of a better solution this might help them.
[WebGet]
public Bid GetHighestBidInOpenAuctions(int auctionEventId)
{
/*
* This method contains a hack that was put in under tight time constraints. The query to
* get bids for all open auctions used to fail when we had a large number of open auctions.
* In this implementation we have fixed this by splitting the open auctions into groups of 20
* and running the query on those 20 auctions and then combining the results.
*/
const int auctionIdSegmentSize = 20;
var auctionEvent = CurrentDataSource.AuctionEvents.Where(x => x.Id == auctionEventId).FirstOrDefault();
var auctionIds = CurrentDataSource.Auctions.Where(x => x.AuctionEventId == auctionEventId && x.Ends > DateTime.UtcNow).Select(x => x.Id).ToList();
int numberOfSegments = auctionIds.Count/auctionIdSegmentSize;
if (auctionIds.Count % auctionIdSegmentSize != 0)
numberOfSegments++;
var bidsList = new List<IQueryable<Bid>>();
for (int i = 0; i < numberOfSegments; i++)
{
int start = i*auctionIdSegmentSize;
int end;
if (i == numberOfSegments - 1)
{
end = auctionIds.Count - 1;
}
else
{
end = ((i + 1)*auctionIdSegmentSize) - 1;
}
var subList = auctionIds.GetRange(start, (end - start) + 1);
bidsList.Add(CurrentDataSource.Bids.Where(b => subList.Any(id => id == b.AuctionId)));
}
// If the auction Event has not yet started or there are no bids then show auction with high pre-sale estimate.
if (IsBidsCountZero(bidsList) || auctionEvent.Starts > DateTime.UtcNow)
{
return null;
}
var highestBid = FindHighestBid(bidsList);
return highestBid;
}
private Bid FindHighestBid(List<IQueryable<Bid>> bidsList)
{
var bids = new List<Bid>();
foreach (var list in bidsList)
{
bids.Add(list.Where(b => b.IsAutobid == false).OrderByDescending(b => b.Amount).FirstOrDefault());
}
bids.RemoveAll(b => b == null);
if (bids.Count == 0)
return null;
bids.Sort(BidComparison);
return bids[0];
}
private int BidComparison(Bid bid1, Bid bid2)
{
if (bid1.Amount < bid2.Amount)
return 1;
if (bid1.Amount > bid2.Amount)
return -1;
return 0;
}
private bool IsBidsCountZero(List<IQueryable<Bid>> bidsList)
{
int count = 0;
foreach (var list in bidsList)
{
count += list.Count();
}
return count == 0;
}
The problem is with auctionIds.Any(t => t == x.AuctionId) where EF cannot create a correct query. You can change it to:
var bids = CurrentDataSource.Bids.Where(x => auctionIds.Contains(x.AuctionId));
Where EF can convert auctionIds to a collection and pass to DB.

conditional where in linq query

I want to return the total sum from a linq query, I pass in 2 parameters that may/may not be included in the query.
OrgId - int
reportType - int
So two questions:
How can I update the query below so that if OrgId = 0 then ignore the organisation field(Return All)?
LocumClaimTF can be True/False/Both, if both then ignore the where query for this field.
Heres what I have done so far, this is working but I'd like something for efficient.
// Using reportType set preferences for LocumClaimTF
bool locumClaimTF1, locumClaimTF2 = false;
if (reportType == 0)
{
locumClaimTF1 = false;
locumClaimTF2 = false;
}
else if (reportType == 1)
{
locumClaimTF1 = true;
locumClaimTF2 = true;
}
else // 2
{
locumClaimTF1 = true;
locumClaimTF2 = false;
}
if (OrgID != 0) // Get by OrgID
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.Where(x => x.Shift.organisationID == OrgID)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g=>g.dataCurrencyAmount);
}
else // Ignore OrgID - Get ALL Orgs
{
return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
.Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
).Sum(g => g.dataCurrencyAmount);
}
I'm using EF with unit of work pattern to get data frm
A few things come to mind, from top to bottom:
For handling the Booleans
bool locumClaimTF1 = (reportType == 1 || reportType == 2);
bool locumClaimTF2 = (reportType == 1);
From what I read in the query though, if the report type is 1 or 2, you want the Shift's LocumClaimTF flag to have to be True. If that is the case, then you can forget the Boolean flags and just use the reportType in your condition.
Next, for composing the query, you can conditionally compose where clauses. This is a nice thing about the fluent Linq syntax. However, let's start temporarily with a regular DbContext rather than the UoW because that will introduce some complexities and questions that you'll need to look over. (I will cover that below)
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.GroupBy(s => s.assignedLocumID)
.Select(g => new dataRowDTO
{
dataLabel = tring.Concat(g.FirstOrDefault().User.FullName),
dataCount = g.Count(),
dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value)
})
.Sum(g=>g.dataCurrencyAmount);
}
Now this here didn't make any sense. Why are you going through the trouble of grouping, counting, and summing data, just to sum the resulting sums? I suspect you've copied an existing query that was selecting a DTO for the grouped results. If you don't need the grouped results, you just want the total. So in that case, do away with the grouping and just take the sum of all applicable records:
var total = query.Sum(x => x.shiftDateTotal.Value);
So the whole thing would look something like:
using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
{
var query = context.ShiftDates
.Where(x => x.shiftStartDate >= StartDate
&& x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;
}
Back to the Unit of Work: The main consideration when using this pattern is ensuring that this Get call absolutely must return back an IQueryable<TEntity>. If it returns anything else, such as IEnumerable<TEntity> then you are going to be facing significant performance problems as it will be returning materialized lists of entities loaded to memory rather than something that you can extend to build efficient queries to the database. If the Get method does not return IQueryable, or contains methods such as ToList anywhere within it followed by AsQueryable(), then have a talk with the rest of the dev team because you're literally standing on the code/EF equivalent of a land mine. If it does return IQueryable<TEntity> (IQueryable<ShiftDate> in this case) then you can substitute it back into the above query:
var query = _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate);
if (reportType == 1 || reportType == 2)
query = query.Where(x.Shift.LocumClaimTF);
if (OrgId > 0)
query = query.Where(x => x.Shift.organisationID == OrgID);
var total = query.Sum(x => x.shiftDateTotal.Value);
return total;