How to properly detect pages in DataTable? - javascript

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.

Related

How to provide an XMLHttpRequest promise to a JavaScript function

Messing around with an autocomplete plugin available at https://www.npmjs.com/package/bootstrap-4-autocomplete, and the following works:
$('#id').autocomplete({
source: {'test1':1, 'test2':2, 'test1':3}
});
Instead of local JSON, will need to make an XMLHttpRequest and was thinking something like the following, and while I don't get an error, I also don't get anything:
$('#id').autocomplete({
source: function() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
return JSON.parse(this.responseText);
}
};
xhttp.open(method, url, true);
xhttp.send();
}
});
The plugin's author made the following remark a while back:
I don't have plans to directly invoke any url inside the lib. What you
can do is set autocomplete to your textfield after your ajax call
returns, which you can do with jQuery, like this:
$.ajax('myurl').then((data) => $('#myTextfield').autocomplete({
source: data }));
You don't have to worry about setting autocomplete to a field multiple
times, it is supposed to work like this when you need to change the
source.
Tried it and as expected, $.ajax() initiated an XMLHttpRequest request upon page load, and not as desired when the user enters a character into the search input.
How am I able to make an XMLHttpRequest to source the data into the plugin? I am assuming that I should be using a promise, however, if not, still would appreciate any assistance.
Thanks
Well, that's how plugin supposed to work. Its meat and potatoes is createItems function, called on keyup event - and responsible for filling out that dropdown with items. And here's its key part (1.3.0 version):
function createItems(field: JQuery < HTMLElement > , opts: AutocompleteOptions) {
const lookup = field.val() as string;
// ...
let count = 0;
const keys = Object.keys(opts.source);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const object = opts.source[key];
const item = {
label: opts.label ? object[opts.label] : key,
value: opts.value ? object[opts.value] : object,
};
if (item.label.toLowerCase().indexOf(lookup.toLowerCase()) >= 0) {
items.append(createItem(lookup, item, opts));
if (opts.maximumItems > 0 && ++count >= opts.maximumItems) {
break;
}
}
}
// skipped the rest
}
As you can see, each time createItems is called, it goes through source object, grepping all the items containing lookup string.
So all the data parts are expected to be there - and to be processable synchronously. That's the plugin's way, with all good and bad coming out of this approach.
The best thing the plugin's author could've suggested here (without going against what plugin is about) is using AJAX to prepopulate the data before calling autocomplete. And that's what he did in that comment actually.
Now, what can be done here? One might think it's enough just to transform createItems into an async function - for example, calling source if it's a function and expecting its result to be a Promise. It seems to be seductively simple excluding that lookup loop in process - and just take the the results of that AJAX call to repopulate source...
But that's not so simple, unfortunately: there are several caveats to be aware of. What should happen, for example, if user stops typing (triggering first AJAX call), then types some more, then stops once again (triggering another AJAX call) - but the first one actually arrives later? The corresponding bug was plaguing a lot of autocomplete implementations I've been working with, sadly - it's not that easy to reproduce if you're testing only with fast network connections (let alone only on localhost).
That's just one of the reasons the author decided against extending that plugin, it seems. After all, it was built to solve one specific task - and it does this well. So unless you want to fork it and essentially rewrite it into 'two strategies' one, I'd suggest considering looking somewhere else.

multiple kendo grids using one script

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.

What is more effective: every time make an ajax request or slice a result in func?

I have a JSON data of news like this:
{
"news": [
{"title": "some title #1","text": "text","date": "27.12.15 23:45"},
{"title": "some title #2","text": "text","date": "26.12.15 22:35"},
...
]
}
I need to get a certain number of this list, depended on an argument in a function. As I understand, its called pagination.
I can get the ajax response and slice it immediately. So that every time the function is called - every time it makes an ajax request.
Like this:
function showNews(page) {
var newsPerPage = 5,
firstArticle = newsPerPage*(page-1);
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
var newsArr = JSON.parse(xhr.responseText),
;
newsArr.news = newsArr.news.slice(firstArticle, newsPerPage*(page));
addNews(newsArr);
}
};
xhr.open("GET", url, true);
xhr.send();
Or I can store all the result in newsArr and slice it in that additional function addNews, sorted by pages.
function addNews(newsArr, newsPerPage) {
var pages = Math.ceil(amount/newsPerPages), // counts number of pages
pagesData = {};
for(var i=0; i<=pages; i++) {
var min = i*newsPerPages, //min index of current page in loop
max = (i+1)*newsPerPages; // max index of current page in loop
newsArr.news.forEach(createPageData);
}
function createPageData(item, j) {
if(j+1 <= max && j >= min) {
if(!pagesData["page"+(i+1)]) {
pagesData["page"+(i+1)] = {news: []};
}
pagesData["page"+(i+1)].news.push(item);
}
}
So, simple question is which variant is more effective? The first one loads a server and the second loads users' memory. What would you choose in my situation? :)
Thanks for the answers. I understood what I wanted. But there is so much good answers that I can't choose the best
It is actually a primarily opinion-based question.
For me, pagination approach looks better because it will not produce "lag" before displaying the news. From user's POV the page will load faster.
As for me, I would do pagination + preload of the next page. I.e., always store the contents of the next page, so that you can show it without a delay. When a user moves to the last page - load another one.
Loading all the news is definitely a bad idea. If you have 1000 news records, then every user will have to load all of them...even if he isn't going to read a single one.
In my opinion, less requests == better rule doesn't apply here. It is not guaranteed that a user will read all the news. If StackOverflow loaded all the questions it has every time you open the main page, then both StackOverflow and users would have huge problems.
If the max number of records that your service returns is around 1000, then I don't think it is going to create a huge payload or memory issues (by looking at the nature of your data), so I think option-2 is better because
number of service calls will be less
since user will not see any lag while paginating, his experience of using the site will be better.
As a rule of thumb:
less requests == better
but that's not always possible. You may run out of memory/network if the data you store is huge, i.e. you may need pagination on the server side. Actually server side pagination should be the default approach and then you think about improvements (e.g. local caching) if you really need them.
So what you should do is try all scenarios and see how well they behave in your concrete situation.
I prefer fetch all data but showing on some certain condition like click on next button data is already there just do hide and show on condition using jquery.
Every time call ajax is bad idea.
but you also need to call ajax for new data if data is changed after some periodic time

Issues with page.JumpToData()

I am using this and have a datatable in my page.
I am receiving values from another site pages, and according to them I should redraw table.
I am receiving BookNo value, and when table is loaded, I should open page where this BookNo is placed. (If it is placed on page 2 or 3, I should open those page).
I found this and tried to use it:
var dtApi = new $.fn.dataTable.Api("#Book_grid");
dtApi.page.jumpToData(selectedBookNo, 1);
but, this has falled with TypeError: dtApi.page.jumpToData is not a function. What am I doing wrong?
EDIT:
Added:
jQuery.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;
});
This was missed. Now I don't have any exceptions, but this does't do a thing at all.
When script goes to setting var pos, I am receiving -1 no matter what, which means, I simply cant receive data? How can I deal with it?
Judging by the code example on the page you linked to, your code should be:
var dtApi = $("#Book_grid").DataTable();
dtApi.page.jumpToData(selectedBookNo, 1);

Can ajax be used for faceting/sorting?

I created a select from with popularity,high_to_low and low_to_high as options. I want the page to respond to these options dynamically using the ajax code
var http_option = createRequestObject();
function verifyRequest()
{
var option = document.getElementById("option").value;
if ( option )
{
var url = 'respond.pl?option='+option;
http_option.open('get', url );
http_option.onreadystatechange = handleResponse;
http_option.send(null);
}
}
function handleResponse()
{
if(http_option.readyState == 4 && http_option.status == 200)
{
var response = http_option.responseText; // Text returned FROM perl script
if(response) { // UPDATE ajaxTest content
document.getElementById("id_id").innerHTML = response;
}
}
If the value of the option is 1, images are displayed as stored in DB.
If the option is 2, images are to be displayed in descending order and
If the option is 3, images are to be displayed in ascending order
The respond.pl contain appropriate code for this sorting according to options and display images in a specified div tag.
The problem is that the page responds to the options only once and the next time on changing the option, the value of options shows "on" and not the numerals 1,2,3
I need this not only to sort images but also for faceting. If this is not the right option suggest the appropriate methods used for it.
It looks like your server-side program is returning HTML. And I think that's probably a mistake in this situation. I suggest returning JSON instead. Then, you can create an onChange event handler for your selector which simply re-orders the display. There are almost certainly a number of jQuery plugins that do this without you needing to write very much code.

Categories