Kendo ui dropdownlist virtualization fails on 2nd page retrieval - kendo-ui

Kendo ui version 2015.2.805
I have a kendo dropdownlist configured per examples in the kendo docs to do paging of data. It works for page 1, loading 80 entries from the server using the take/skip parameters. As I page down the list to where new items should be loaded I can see in fiddler that the widget is requesting take=80, skip=80 (which is page 2) and the server is returning the requested records. But the widget just keeps re-requesting the same page over and over until I stop it. During this time the unloaded entries are shown in the list as 'loading' with a animated spinner.
To do virtualization the widget requires a valueMapper (I am using the older kendo UI so it is required). I have implemented it and while I am unclear on what it should return (the item index I believe) I know from the docs that if it requests a non-existent value to return [], and that is what I am doing in that case. I modified the convertValues function to send only a single index instead of the example array, but the valueMapper is only called once upon initialization so any errors in what I'm returning can't have any effect on this problem (I believe).
When the widget is first initialized in fiddler I see that the valueMapper is called with a value of -1 and the server returns [], then the widget calls for the paged data (take=80, skip=0) and the server returns that and the widget displays the data normally.
When I page down to unloaded items, the widget calls for page two data (take=80, skip=80) and the server returns it but the widget keeps re-requesting the data. The widget never calls the valueMapper after the first call (which may be normal).
I have the page size set correctly for the height and itemHeight, but this would only cause page one to be loaded twice (which it is not).
Here is the setup:
wizard.paramMap = function (data, type) {
var params = {};
params.take = data.take;
params.skip = data.skip;
return params;
};
$("#clientField").kendoDropDownList({
autoWidth: true,
dataTextField: "text",
dataValueField: "value",
virtual: {
itemHeight: 26,
valueMapper: function(options) {
$.ajax({
url: resturi + "clientlist/valueMapper",
dataType: "json",
type: "GET",
data: convertValues(options.value),
success: function (data) {
console.log("valueMapper = " + data)
options.success(JSON.parse(data));
}
})
}
},
height: 520,
dataSource: {
transport: {
read: {
url: resturi + "clientlist",
dataType: "json",
type: "get"
},
parameterMap: wizard.paramMap
},
schema: {
data: function (response) {
console.log("clientlist = " + response);
var o = JSON.parse(response);
return o;
}
},
pageSize: 80,
serverPaging: true
}
});
function convertValues(value) {
var data = {};
value = $.isArray(value) ? value : [value];
for (var idx = 0; idx < value.length; idx++) {
//data["values[" + idx + "]"] = value[idx];
data["values"] = value[idx];
break;
}
return data;
}

The data returned by the datasource must return a total record count along with the paged data. This is the same as for paging in the kendo datagrid. So changing server output to:
{"total":X,"data":[...some data...]}
and the schema to:
schema: {
total: function (response) {
return (JSON.parse(response).total);
},
data: function (response) {
return (JSON.parse(response).data);
}
}
fixes the problem.

Related

Slick grid values not populating in the grid

I am trying to populate slick grid it is showing the values while debugging but not showing in the web page.
I have added all the required references. Here is the js function.
function LoadMonthStatus(dropdownYear, buttonId) {
try {
$(".screen").css({ opacity: 0.5 });
$(".slick-cell").css({ opacity: 0.5 });
dirtyFlag = false;
var drpYear = document.getElementById(dropdownYear);
year = drpYear.options[drpYear.selectedIndex].value;
data = [];
var dropdownoptions = ""; // "hard,soft,closed";
var columns = {};
var options = {
editable: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: true,
autoHeight: true
};
columns = [
{ id: "month", name: "Month", field: "Month", width: 250 },
{ id: "status", name: "Close Status", field: "Status", options: columnOptions, editor: Slick.Editors.Select, width: 150 }
];
$.ajax({
type: "GET",
url: "http://localhost:51072/PULSE.Service/api/MonthCloseStatus/LoadMonths",
data: { year: $("#drpYear").val() },
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
data = msg;
monthStatusGrid = new Slick.Grid("#slkgrdMonths", data, columns, options);
//grid.setSelectionModel(new Slick.CellSelectionModel());
monthStatusGrid.onCellChange.subscribe(function (e, args) {
var cell = monthStatusGrid.getCellNode(args.row, args.cell);
cell.className += cell.className ? ' slick-cell-modified' : 'slick-cell-modified';
dirtyFlag = true;
});
if (msg == null || msg == "")
document.getElementById(buttonId).disabled = true;
else
document.getElementById(buttonId).disabled = false;
//Enable the button
$(".screen").css({ opacity: 1 });
$(".slick-cell").css({ opacity: 1 });
},
error: function (xhr, textStatus, errorThrown) {
try {
var err = JSON.parse(xhr.responseText);
jAlert(err.Message, 'PULSE');
}
catch (e) {
jAlert(clientErrMessage, 'PULSE');
}
$(".screen").css({ opacity: 1 });
$(".slick-cell").css({ opacity: 1 });
}
});
}
catch (e) {
jAlert(clientErrMessage, 'PULSE');
}
}
Slick grid should be populated with months like Jan, Feb, March and its status respectively in 2 columns.
There are too many variables to give an answer. However I would recommend that you create the grid on page load and just populate it with data on ajax load, rather than creating the grid possibly each time.
In my experience, data not being visible is often CSS related.
Have a look at the samples in the SlickGrid repo, they are pretty comprehensive. Make sure you are using https://github.com/6pac/SlickGrid rather than the old MLeibman repo.
If you want assistance, the best idea is to create a stand alone test page that works with local files (apart from the ajax call) in the example folder of the slickgrid repo.
If you can share that, we can easily reproduce the behaviour. As a bonus, about 90% of the time you'll work out the problem while you are creating the example page.
Check to see
-if the property of data after ajax call is matching with the property provided in columns Field and vice versa.
- if the container u are passing is correct.

free-jqGrid 4.15.6 ExpandNode producing runtime error

I recently upgraded from the original Tony Tomov's jqGrid v4.5.4 to Oleg's free-jqGrid 4.15.6.
When using 4.5.4, the code below worked perfect, but in 4.15.6, it does not. The run-time error is produced by the two calls to expandNode and expandRow located in the forceNodeOpen() function. The error given:
TypeError: rc1 is undefined
The forceNodeOpen() function is used to force all ancestor nodes of the current treegrid node to show as expanded. Much like a table of contents...if we load an initial topic node, we want the whole topic hierarchy to be expanded:
// force a node (if expandable) to stay expanded
function forceNodeOpen(rowid)
{
var record = $("#tree").jqGrid('getRowData', rowid);
var div = $('tr#'+rowid).find('div.ui-icon.treeclick');
div.removeClass('ui-icon-triangle-1-e tree-plus').addClass('ui-icon-triangle-1-s tree-minus');
**$('#tree').jqGrid('expandNode', record);**
**$('#tree').jqGrid('expandRow', record);**
// get all ancestoral parents and expand them
// *NOTE*: the getAncestorNodes function of grid was not usable for
// some reason as the same code below just would not work
// with the return array from getAncestorNodes
var parent = $("#tree").jqGrid('getNodeParent', record);
while(parent)
{
forceNodeOpen(parent['id']);
parent = $("#tree").jqGrid('getNodeParent', parent);
}
}
// using topic url, get the tree row id
function getTopicID(topic)
{
var nodes = $('#tree').jqGrid('getRowData');
var rowid = 1;
$.each(nodes, function(e,i)
{
var url = $(this).attr('url');
if(url == topic)
{
rowid = $(this).attr('id');
return false;
}
});
return rowid;
}
// post request to help server via ajax
function loadTopic(topic)
{
// no need to load again
if(loadedtopic == topic) { return false; }
// select the topic node
var rowid = getTopicID(topic);
loading = true;
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
loading = false;
// wipe content
$('h1#help_content_topic span:first').html('Loading...');
$('div#help_content').html('');
// block UI for ajax posting
blockInterface();
// request help content
$.ajax(
{
type: 'POST',
url: '/index.php',
data: { 'isajax': 1, 'topic': topic },
success: function(data)
{
$.unblockUI();
$('h1#help_content_topic span:first').html(data['topic']);
$('div#help_content').html(data['content']);
return false;
}
});
// save current topic to prevent loading same topic again
loadedtopic = topic;
}
// table of contents
$('#tree').jqGrid({
url: "topics.php",
datatype: "xml",
autowidth: true,
caption: "Help Topics",
colNames: ["id","","url"],
colModel: [
{name: "id",width:1,hidden:true, key:true},
{name: "topic", width:150, resizable: false, sortable:false},
{name: "url",width:1,hidden:true}
],
ExpandColClick: true,
ExpandColumn: 'topic',
gridview: false,
height: 'auto',
hidegrid: false,
pager: false,
rowNum: 200,
treeGrid: true,
treeIcons: {leaf:'ui-icon-document-b'},
// auto-select topic node
gridComplete: function()
{
// save current topic to prevent loading same topic again
loadedtopic = '<? echo($topic) ?>';
var rowid = getTopicID('<? echo($topic) ?>');
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
$.unblockUI();
},
// clear initial loading
loadComplete: function()
{
loading = false;
},
onSelectRow: function(rowid)
{
// ignore initial page loads
if(loading) { return false; }
// load the selected topic
var topic = $("#tree").jqGrid('getCell',rowid,'url');
loadTopic(topic);
}
});
The forceNodeOpen(rowid) is invoked from the loadTopic() function, which is called inside the onSelectRow() event of the treegrid.
Not sure what 4.5.4 did that allowed this code to work but 4.15.6 finds it to be an error. The offending line in 4.15.6.src.js:
expandNode: function (rc) {
...
if (p.treedatatype !== "local" && !base.isNodeLoaded.call($($t), p.data[p._index[id]]) && !$t.grid.hDiv.loading) {
// set the value which will be used during processing of the server response
// in readInput
p.treeANode = rc1.rowIndex;
p.datatype = p.treedatatype;
...});
I have only included a few lines from the above core function. It's the p.treeANode = rc1.rowIndex that throws the error.
I have to be missing something but do not know what. Hoping somebody can tell me what to do. If I remark out the two expandNode and expandRow treegrid function calls in forceNodeOpen() function, the system does not error out and the desired topic loads. But the hierarchy is not expanded as desired.
START EDIT 1
The server-side code that returns the topic nodes:
echo("<?xml version='1.0' encoding='UTF-8'?>\n");
require('db.php');
echo("<rows>\n");
echo("<page>1</page>\n");
echo("<total>1</total>\n");
echo("<records>1</records>\n");
$sql = "SELECT node.id, node.parentid, node.topic, node.url, node.lft, node.rgt, (COUNT(node.parentid) - 1) AS depth
FROM helptopics AS node, helptopics AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.url ORDER BY node.lft";
$stmt = AMDB::selectStatement($sql);
while($data = $stmt->fetch(PDO::FETCH_ASSOC))
{
$id = $data['id'];
$pid = $data['parentid'];
$topic = $data['topic'];
$url = $data['url'];
$lft = $data['lft'];
$rgt = $data['rgt'];
$leaf = $rgt - $lft == 1 ? 'true' : 'false';
$exp = 'false';
$dep = $data['depth'];
echo("<row><cell>{$id}</cell><cell>{$topic}</cell><cell>{$url}</cell><cell>{$dep}</cell><cell>{$lft}</cell><cell>{$rgt}</cell><cell>{$leaf}</cell><cell>{$exp}</cell></row>\n");
}
echo("</rows>\n");
exit();
END EDIT 1
I see that you use
var record = $("#tree").jqGrid('getRowData', rowid);
to get node data of TreeGrid. It wasn't good, but it worked in old jqGrid because it saved internal jqGrid data twice: once as local data and once more time in hidden cells of the grid.
You should use getLocalRow instead of getRowData:
var record = $("#tree").jqGrid('getLocalRow', rowid);
The code will work on both old jqGrid and free jqGrid. Additionally, getLocalRow has always better performance comparing with getRowData even in jqGrid 4.5.4.

Youtube search autocomplete not working with Framework 7

I am trying to use Framework 7 autocomplete feature with youtube search API v3. I had used search api for autocomplete using Jquery UI. Framework 7 has also Ajax autocomplete feature. But my code is not working with Framework 7.
Here is my youtube search autocomplete js code for jquery UI, that works 100% and shows up video search suggestion on text input
//code for auto complete using jquery UI works perfect
jQuery( "#vid-search" ).autocomplete({
source: function( request, response ) {
//console.log(request.term);
var sqValue = [];
jQuery.ajax({
type: "POST",
url: "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1",
dataType: 'jsonp',
data: jQuery.extend({
q: request.term
}, { }),
success: function(data){
console.log(data[1]);
obj = data[1];
jQuery.each( obj, function( key, value ) {
sqValue.push(value[0]);
});
response( sqValue);
}
});
},
select: function( event, ui ) {
setTimeout( function () {
youtubeApiCall();
}, 300);
}
});
And here is my youtube search autocomplete code with framework 7, Doest show up video search suggestions on text inpute..
var autocompleteDropdownAjax = myApp.autocomplete({
input: '#autocomplete-dropdown-ajax',
openIn: 'dropdown',
preloader: true, //enable preloader
valueProperty: 'id', //object's "value" property name
textProperty: 'name', //object's "text" property name
limit: 20, //limit to 20 results
dropdownPlaceholderText: 'Try "JavaScript"',
expandInput: true, // expand input
source: function (autocomplete, query, request, response, render) {
var results = [];
if (query.length === 0) {
render(sqValue);
return;
}
// Show Preloader
autocomplete.showPreloader();
// Do Ajax request to Autocomplete data
$$.ajax({
type: "POST",
url: "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1",
dataType: 'jsonp',
data: jQuery.extend({
q: request.term
}, { }),
success: function (data) {
// Find matched items
console.log(data[1]);
obj = data[1];
jQuery.each( obj, function( key, value ) {
sqValue.push(value[0]);
});
response( sqValue);
// Hide Preoloader
autocomplete.hidePreloader();
// Render items by passing array with result items
render(sqValue);
}
});
},
select: function( event, ui ) {
setTimeout( function () {
youtubeApiCall();
}, 300);
}
});
That is because the jSon result should be in a special format, the result rendered by the API looks like:
["funn",["funny vines","funny videos 2016","funny videos","funny","funnel vision","funny cat videos","funny fails","funny pranks","funny cats","funny songs"]]
First parent array has the query you typed, second child array has the result array, you must push the child array to the result.
for (var i = 0; i < data[1].length; i++) {
results.push(data[1][i])
}
And here is the result:
Full code:
var autocompleteDropdownAjax = myApp.autocomplete({
input: '#autocomplete-dropdown-ajax',
openIn: 'dropdown',
preloader: true, //enable preloader
valueProperty: 'value', //object's "value" property name
textProperty: 'text', //object's "text" property name
limit: 20, //limit to 20 results
dropdownPlaceholderText: 'Search Youtube',
expandInput: true, // expand input
source: function(autocomplete, query, render) {
var results = [];
var returned = [];
if (query.length === 0) {
render(results);
return;
}
// Show Preloader
autocomplete.showPreloader();
// Do Ajax request to Autocomplete data
$$.ajax({
url: 'http://suggestqueries.google.com/complete/search?client=firefox&ds=yt',
method: 'GET',
crossDomain: true,
dataType: 'json',
//send "query" to server. Useful in case you generate response dynamically
data: {
q: query
},
success: function(data) {
// Find matched items
for (var i = 0; i < data[1].length; i++) {
results.push(data[1][i])
}
// Hide Preoloader
autocomplete.hidePreloader();
// Render items by passing array with result items
render(results);
}
});
}
});
I hope this helps :)

SELECT2 empty results

I'm creating a simple select2 search box with a remote request to a php page.
I choosed to use "json" instead of "jsonp" and it seems correctly working.
The problem is that the results are white: no writes as element list.
In the image that I cannot post because "I need at least 10 reputation to post"... -.-
You could see the input box with the letter "d" and:
no loading icon (because the search has correctly ended)
no "Search" write (because the search has correctly ended)
the search box height is exactly for 2 results and you can select the results but the text is invisible.. you see just the blue selection..
The code follows:
function MultiAjaxAutoComplete(element, url) {
$(element).select2({
minimumInputLength: 1,
ajax: {
url: url,
dataType: 'json',
data: function(term, page) {
return {
q: term,
page_limit: 10
};
},
results: function(data, page) {
return {
results: data
};
}
},
formatResult: formatResult,
formatSelection: formatSelection,
initSelection: function(element, callback) {
var data = [];
$(element.val().split(",")).each(function(i) {
var item = this.split(':');
data.push({
id: item[0],
title: item[1]
});
});
//$(element).val('');
callback(data);
}
});
};
function formatResult(movie) {
return '<div>' + movie.title + '</div>';
};
function formatSelection(data) {
return data.title;
};
MultiAjaxAutoComplete('#advertiser', '/AJAXController');
and the returned json data is the following
[{"id":"12889","title":"Donnie Darko" },
{"id":"8", "title":"Another title"}
]
PS: I got two results because, at the moment, I'm returning the json data as direct output text with "echo <<< EOF ..."
Thank you,
Mauro
var data = [
{ "id":"12889", "title":"Donnie Darko" },
{ "id":"8", "title":"Another title"}
]
$(element).select2('data', data);

Loading 10k+ rows of JSON into jqGrid 1k rows at a time

Introduction
I load a JSON object via ajax into the browser outside of jqGrid. I then load the JSON into a grid, which the initial setting datatype: "local", using the following:
$("#myGrid").jqGrid("setGridParam", {datatype: "json", loadonce: true});
$("#myGrid")[0].addJSONData(jsonObject);
$("#myGrid").jqGrid("setGridParam", {datatype: "local", loadonce: true});
The above tricks the grid into allowing me to load a JSON object without the jqGrid making an automatic call for it (which is its default behavior).
Implementing server-side pagination would be ideal, but not currently feasible in my case.
The Question
I need to load about 10-20k rows into the grid from JSON. But that takes several seconds. To boost performance, I'm trying to add 1k rows from a JSON object at a time (which is fast and doesn't stall the browser), but I can't figure out how to do that with a JSON object on my hands. Here is my attempt, which loads the first 1k rows into the grid, but doesn't properly update the pager, and doesn't allow me to add additional rows via addJSONData.
$("#myGrid").jqGrid({
jsonReader: {
page: function(json) { return 1; },
records: function(json) { return json.records; },
repeatitems: false,
root: "objects",
total: function(json) { return json.totalPages; }
},
// . . .
}
$.jgrid.extend({
loadJsonFirstPageOnly: function(json) {
return this.each(function() {
// copy the first page of data into a truncated JSON object to load into the grid
var resultsPerPage = $("#" + this.id + "_ResultsPerPage").val();
var numberOfPages = parseInt(json.objects.length / resultsPerPage);
var firstPageData = {"objects": [], "pages": numberOfPages, "records": json.objects.length};
for (var i = 0; i < 1000 && i < json.objects.length; i++) {
firstPageData.objects[i] = json.objects[i];
}
// load the truncated JSON object into the grid
var $this = $(this).jqGrid("setGridParam", {datatype: "json", loadonce: true});
this.addJSONData(firstPageData);
$this.jqGrid("setGridParam", {datatype: "local", loadonce: true});
// adjust the grid's internal state
this.p.allData = json;
});
}
});
Any thoughts? Thanks!

Resources