Whenever a user changes the pagination on a grid, I save the setting in localStorage and retrieve it to set it back whenever the user navigates again to the page. To retrieve it I am using the pageSize property of the dataSource where I pass an IIF like so:
pageSize: function () {
var pageSize = 10;
if (window.localStorage) {
var userPreferencePageSize = localStorage.getItem("somegridPageSize");
if (userPreferencePageSize === parseInt(userPreferencePageSize)) {
userPreferencePageSize = parseInt(userPreferencePageSize);
}
if (!isNaN(userPreferencePageSize)) {
pageSize = userPreferencePageSize;
}
}
return pageSize;
}()
This worked well but a requirement appeared for the user to be able to set the pageSize to "All". Kendo handles "All" in the Grid so I thought this will let me set the dataSource pageSize to the string "All" (pageSize="All") as well. When I do it however the grid starts displaying NaN of X records and displays an empty grid. So the question is.. how do I preset the pageSize of the dataSource to just "All"?
NOTE: An alternative is to just fetch the grid maximum total count and then replace the number displayed in the dropdown with jquery text("All) but that looks like a hack and it seems to me this should already be inbuilt into the framework but I can't find anything in the doc's.
EDIT: This is getting even funnier. Due to lack of other options I implemented it like in the note and just set the dataSource pageSize directly:
$("#Grid").data("kendoGrid").dataSource.pageSize(pageSize);
But this is causing the filters on grid to malfunction and throw "string is not in correct format" (misleading error) error from the endpoint. After enough research I found out its caused by the DataSourceRequest doing some unidentifiable shuru buru in the background. Since setting the dataSource pageSize causes issues, I tried just setting the dropdown programatically and let kendo trigger the request to update pageSize itself like so:
var selectBox = $(".k-pager-sizes").find("select");
selectBox.val(pageSize);
selectBox.find("option[value='" + pageSize + "']").prop('selected', true);
But the grid doesn't let me do it and keeps reverting any changes I do inside the DOM from javascript.
So the question is, how in earth can you change the pageSize of a server-side kendo grid from javascript (triggering an extra request to endpoint).
To answer the question. This appears to be a bug in Kendo. Setting the pageSize to 0 (shortcut for "all") after the dataSource was already bound will always result in the grid having issues with any custom filters declared inside of a toolbar template.
To be exact, if you have a custom filter defined inside of the toolbar template:
#(Html.Kendo().TextBox().Name("Filter").Deferred())
You wire it up through the dataSource definition on the grid like:
.DataSource(ds =>
ds.Ajax()
.PageSize(defaultPageSize)
.Read(a => a.Action(actionName, controllerName, new
{
param = Model.param
}).Data("getFilterParameters")))
in javaScript fetching the parameters like:
getFilterParameters: function () {
return this.filterParameters;
},
populating them with a method:
filter: function () {
var grid = $("#Grid").data("kendoGrid");
this.filterParameters = {
param: $("#Filter").val()
};
grid.dataSource.page(1);
}
that has a simple event listener wired to it:
$("#Filter").on("keyup", filter);
Then after changing the pageSize programatically to 0/"all" with:
$("#Grid").data("kendoGrid").dataSource.pageSize(0);
The filter will start to always return NaN as the current page / skip of the filter object passed to the server with the query parameters (even though grid will display the numbers correctly). This will cause a "string is not in correct format" exception inside framework code on the endpoint whenever you try using the filter. A solution to the above is to slightly modify the getFilterParameters method:
getFilterParameters: function (e) {
// Workaround for Kendo NaN pager values if pageSize is set to All
if (isNaN(e.page)) e.page = 1;
if (isNaN(e.skip)) e.skip = 0;
//
return this.filterParameters;
}
This will re-initialize the page and skip values before submitting the filter request. These values will anyway be re-populated on the endpoint with the correct values. Wasn't me who noticed it but another developer working on the project, credit goes to her.
Related
I'm having an issue when using the material paginator. It retrieves the data correctly and the paginator data is updating correctly specifically the pageIndex is incrementing correctly but the items number does not update as shown below. I believe this is because of the onChangedPage($event) function because when I remove it everything works correctly but my data is of course not updated then.
screen shot of the item count
I'm wondering what could be causing this to not work considering the pageIndex is working correctly here is my code for the paginator. My code is dynamic.
html:
<mat-paginator
[length]="totalMessages"
[pageSize]="messagesPerPage"
[pageSizeOptions]="pageSizeOptions"
(page)='onChangedPage($event)'
></mat-paginator>
TS:
onChangedPage(pageData: PageEvent) {
console.log(pageData);
this.currentPage = pageData.pageIndex + 1;
const endingIndex = this.messagesPerPage * this.currentPage;
const startingIndex = endingIndex - this.messagesPerPage;
this.isLoading = true;
console.log(endingIndex);
console.log(startingIndex);
this.Service
.getFunction(this..ticketNumber, startingIndex, endingIndex)
.subscribe(data => {
****taken out for security reasons
}
Summary: Essentially I am paginating over an embedded array in a Mongo DB document and using the $slice method. My backend logic is fine I'm retrieving everything correctly but for some reason the paginator is not updating the position of the items I'm retrieving. Any help would be greatly appreciated thank you!
If anyone runs into this type of issue. I figured out the issue was because I was using a loading spinner during my call to the back-end for data. This changed the view and reset the page data. Once I removed the spinner it now works as expected.
I'm loading multiple partial views into the same cshtml page. All goes well until they need to use the scripts. As i'm using code like
var grid = $("#grid").data("kendoGrid");
var selected = grid.selected();
This code works fine with one grid, but starts showing issues when multiple grids are in place. The problem is that "#grid" is a reference to the name of the kendo grid. Is there a way to make this dynamic so it can be used by multiple grids?
I think the same problem would occur when there are multiple grids in the same page as it can't distinct what grid to refer to. Giving the grids different id's would work, but then the code in the script will return an undefined error on grid.selected().
Update:
So the solution of using
var grid = $(".k-grid").data("kendoGrid");
works to a certain point. It loads the data into the grid, but fails to do anything else. For example a part of my code for enabling an update and delete button doesn't work on the 2nd and 3rd partial view.
var grid = $(".k-grid").data("kendoGrid");
var selected = grid.select();
if (selected.length > 0) {
$("#btnCopy,#btnEdit,#btnDelete").removeClass("k-state-disabled");
} else {
$("#btnCopy,#btnEdit,#btnDelete").addClass("k-state-disabled");
}
Somehow the code only starts working for grid 2 and 3 after i have selected a row on grid 1, which is not as intended.
Instead of id(#Grid) you can use class(.k-grid):
var grid = $(".k-grid").data("kendoGrid");
The solution I found with help of a senior programmer is to save the grid data into a global variable like this.
var PartialGridData = PartialGridData || {};
After that I'm setting and changing the variable whenever changing the partial view.
PartialGridData.selectedGrid = $("#PartialGrid1").data("kendoGrid");
Where the name #PartialGrid1 is the name of the current grid.
This means I need to write this code as many times as I have grids, but it also fixes a lot of problems. After that I use it to select the correct data.
var grid = PartialGridData.selectedGrid;
var selected = grid.select();
if (selected.length > 0) {
$("#btnCopy,#btnEdit,#btnDelete").removeClass("k-state-disabled");
} else {
$("#btnCopy,#btnEdit,#btnDelete").addClass("k-state-disabled");
}
Another option would be to use e.sender.
function onRowSelect(e) {
var grid = e.sender;
var selected = grid.select();
if (selected.length > 0) {
$("#btnCopy,#btnEdit,#btnDelete").removeClass("k-state-disabled");
} else {
$("#btnCopy,#btnEdit,#btnDelete").addClass("k-state-disabled");
}
}
Both solutions have their drawbacks though. Not all methods get the variable e used for e.sender and changing partial views in a way that is not caught will cause the global variable not to be updated, so this has to be kept in check.
I am using DataTables.
What I am trying to do is: by using one of the columns values, get page number, where this value is located.
I have tried this: jumpToData()
BUT this didn't work out. The reason is that
var pos = this.column(column, { order: 'current' }).data().indexOf(data);
in jQuery.fn.dataTable.Api.register('page.jumpToData()' returns value >=0 ONLY if I was placed on page where value was.
For example, I want to detect page where needed value is, but I am staying on another page, so to detect value on... page 3, I need to go to this page and only then I can detect it, which makes no sence at all.
What I need to do, is: by staying on pirst page, using value from another pages, detect those pages numbers and then navigate to them:
$('#Grid_grid').DataTable().page(PageNumber).draw(false);
How can I accomplish that?
EDIT:
Got some idea (several changes in jumpToData()):
jQuery.fn.dataTable.Api.register('page.jumpToData()', function (data, column) {
for (var i = 0; i < this.page.info().pages; i++) {
var test = this.page(i).column(column, { order: 'current' }).data().indexOf(data);
if (test >= 0) {
this.page(i).draw(false);
return this;
}
}
return this;
});
(EDIT 2: idea didn't paid off, no difference)
BUT now I got second issue:
None methods of datatable works in .cshtml page.
For example I need to get overall page count. I doing this:
$('#Grid_grid').DataTable().page.info().pages;
and this return me 0;
Meanwhile, putting it in to console (Chrome F12) works fine (returns 5). Whats the matter?
EDIT 3:
Came up with this:
function LoadPage(value) {
var table = $('#Grid_grid').DataTable();
var pageNumber = table.search(value).page();
table.page(pageNumber).draw(false);
}
Looks promising BUT, I still cant validate it because in console DataTable methods are working, but in .cshtml no. (search() or page() returns nothing).
EDIT 4:
Moved issue to another question
CAUSE
Your new API method page.jumpToData() tries to query all pages data because second argument selector-modifier in column() API method has property page: 'all' by default. As written it will always stay on first page.
SOLUTION
There is original page.jumpToData() plug-in posted by Allan Jardine, creator of DataTables. It works as intended and can be used instead of your modification to avoid unnecessary iterations.
$.fn.dataTable.Api.register('page.jumpToData()', function (data, column) {
var pos = this.column(column, {
order: 'current'
}).data().indexOf(data);
if (pos >= 0) {
var page = Math.floor(pos / this.page.info().length);
this.page(page).draw(false);
}
return this;
});
DEMO
See this jsFiddle for code and demonstration.
NOTES
In the demo above I added console.log("Number of pages", table.page.info().pages); just to demonstrate that API method works. However they may work because I have HTML-sourced data.
If you have Ajax-sourced data, you need to query number of pages only when data has been loaded. Use initComplete option to define a callback function that will be called when your table has fully been initialised, data loaded and drawn.
I have the following code that builds a grid using slickgrid.js.
var grid;
var gridDataview;
var gridOptions = {
enableCellNavigation: true,
enableColumnReorder: true,
forceFitColumns: false,
topPanelHeight: 25
};
function createGrid(data) {
gridDataview = new Slick.Data.DataView({ inlineFilters: true });
grid = new Slick.Grid("#grid", gridDataview, data.columns, gridOptions);
grid.setSelectionModel(new Slick.RowSelectionModel());
var pager = new Slick.Controls.Pager(gridDataview, grid, $("#pager"));
var columnpicker = new Slick.Controls.ColumnPicker(data.columns, grid, gridOptions);
grid.onSort.subscribe(function (e, args) {
sortdir = args.sortAsc ? 1 : -1;
sortcol = args.sortCol.field;
// using native sort with comparer
// preferred method but can be very slow in IE with huge datasets
gridDataview.sort(comparer, args.sortAsc);
});
// if you don't want the items that are not visible (due to being filtered out
// or being on a different page) to stay selected, pass 'false' to the second arg
gridDataview.syncGridSelection(grid, true);
$("#gridContainer").resizable();
}
I am using this with knockout-js and initially only create the grid after the user makes a selection from a listbox, at which point i fetch data from an rest service and build the grid. each subsequent user selection will not create the grid, only update the data.
self.selectedInstrument.subscribe(function (newValue) {
$.getJSON('/data/' + self.selectedCategory().id + '/' + newValue.id, function (data) {
self.cotData(data);
if (grid == null) {
debugger;
createGrid(data);
}
//gridDataview.beginUpdate();
gridDataview.setItems(data.data);
//gridDataview.endUpdate();
});
});
What's happening is:
1. when the grid is initially create, no data is shown, just the column headers. if i move re-order a column header, then the data is shown.
2. If i sort a column, the sort is not visibly reflected. if i start scrolling, then i see the sort being reflected.
3. if i add a grid.render() to the end of the subscription handler above, i do see the data immediately, but then i'm not able to vertically scroll any longer. things seem badly broken at this point.
Any thoughts on what may be happening here? This started happening after i modified the code to create a DataView rather than loading the data right into the grid immediately. I need a DataView because i want to allow sorting and later different types of aggregation and groupings.
Is this possibly related to usage of slickgrid with knockout js?
Thanks much
Not sure why yet, as i'm still feeling my way around SlickGrid, but i had to add the following two subscriptions. the first allowed the grid to display rows immediately when new data is loaded and the second solved a similar issue, but when sorting:
// wire up model events to drive the grid
gridDataview.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
gridDataview.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
I am using the bootstrap multiselect from https://github.com/davidstutz/bootstrap-multiselect.
Problem Scenario: Based on the users input, I am receiving the options to be included through an ajax call.
I want to include the select all and filter only when there are more than 1 option. I am trying to add the select all and filter properties dynamically. But the problem is, the multiselect seems to remember the properties I set in the previous call.
When the page is loaded, the ajax call returns 3 options, the select all and filter are enabled for the multiselect. But when the user changes input, the ajax call now returns only 1 option. I am updating the function params as in (a) and trying to recreate the multiselect as in (b)
(a) function updateParams(options, params){
if (options.length<1 ){
params.includeSelectAllOption = false;
params.enableFiltering = false;
params.enableCaseInsensitiveFiltering = false;
} else {
params.includeSelectAllOption = true;
params.enableFiltering = true;
params.enableCaseInsensitiveFiltering = true;
}
return params;
}
(b) {....
// $(ele).multiselect('destroy');
$(ele).html('');
var params = $.extend(true, {}, paramsGiven);
params = updateParams(options, params);
$(ele).multiselect(params);
$(ele).multiselect("rebuild");
...}
I copied the params so that they donot persist between user inputs. I printed the params and paramsGiven before and after updating and they carry the properties as I expected. The above code does not seem to recreate the multiselect with new params. I also tried destroying and recreating but it just destroys and does not recreate.
Could anyone please help me understand whats going wrong here?