Extending of jqGrid. Custom parameter - jqgrid

I want to use several jqGrids on one page. All grids will have specific functionality. For this reason I want to extend jqgrid
$.jgrid.extend({
cVal: false,
cSetVal: function(value) {
var $t = this;
$t.cVal = value;
console.log('Setting cVal');
return this;
},
cGetVal: function(value) {
var $t = this;
console.log('Getting cVal');
return $t.cVal;
},
});
After loading of page, I use Firefox console to test
>>> $('#some-selector').jqGrid.cVal;
false
So jqGrid returns default value;
>>> $('#some-selector').jqGrid('cSetVal', 'abc');
Setting cVal //console.log returns
Object[table#some-selector.some-class]
When I browse to Object cVal is 'abc'
But
>>> $('#some-selector').jqGrid('cGetVal', 'abc');
Getting cVal //console.log returns
false
now get defaul value again :(
How could I get value which was set in cSetVal method?

Sorry, but I don't full understand the goal of "extending of jqGrid". If you need to hold some additional information with jqGrid you can just use additional parameters which is not in the list of standard jqGrid parameters. For example if you would create the grid using
$('#some-selector').jqGrid({
url: "someUrl",
datatype: "json",
... // other standard jqGrid parameters
cVal: false,
cSetVal: function(value) {
console.log('cSetVal');
}
);
you will be able to access to the parameters using $('#some-selector').jqGrid('getGridParam', 'cVal'); and $('#some-selector').jqGrid('getGridParam', 'cSetVal'); or just using $('#some-selector')[0].p.cVal and $('#some-selector')[0].p.cSetVal. I think that you don't really need any cSetVal or cGetVal functions and can use getGridParam and setGridParam instead.
To change the value of cVal option if it's defined in the abave way you can use
$('#some-selector').jqGrid('setGridParam', {cVal: true});
or just use
$('#some-selector')[0].p.cVal = true;
Alternatively you can use jQuery.data to store any data associated with $('#some-selector').
$.jgrid.extend is helpful if you need to have new methods which you need call in the form $('#some-selector').jqGrid('cSetVal',value);. The answer shows an example of such implementation and discuss a little advantages and disadvantages of the approach.
UPDATED: So if you really need to use the syntax like $('#some-selector').jqGrid('cSetVal',value); then you should modify your code to
$.jgrid.extend({
cSetVal: function(value) {
console.log('Setting cVal');
$(this).jqGrid('setGridParam', {cVal: value});
return this;
},
cGetVal: function(value) {
console.log('Getting cVal');
return $(this).jqGrid('getGridParam', 'cVal');
}
});
If you need initialize some cVal value for the grid you need just include cVal in the list of option of jqGrid during creating of it:
$('#some-selector').jqGrid({
url: "someUrl",
datatype: "json",
... // other standard jqGrid parameters
cVal: true // HERE
);
UPDATED 2: If you defines methods like I suggested in the first part of my answer you should use the methods in the following way:
var $grid = $('#some-selector');
// create the grid
$grid.jqGrid({
url: "someUrl",
datatype: "json",
... // other standard jqGrid parameters
cVal: false,
cSetVal: function(value) {
console.log('in cSetVal()');
//this.p.cVal = value;
$(this).jqGrid('setGridParam', {cVal: value});
},
cGetVal: function() {
console.log('in cGetVal()');
//return this.p.cVal;
return $(this).jqGrid('getGridParam', 'cVal');
}
);
// now we can use cVal, cSetVal and cGetVal
var cSetVal = $grid.jqGrid("getGridParam", "cSetVal"),
cGetVal = $grid.jqGrid("getGridParam", "cGetVal"),
cVal = $grid.jqGrid("getGridParam", "cVal"); // get cVal value
cSetVal.call($grid[0], true); // change cVal value
cVal = $grid.jqGrid("getGridParam", "cVal");// get modified cVal value
cSetVal.call($grid[0], false);// change cVal value
cVal = cGetVal.call($grid[0]);// get modified cVal value

Related

What causes jqgrid events to fire multiple times?

Our jqgrid is configured in an initgrid function that is called as the last statement of a ready handler. For some reason, the gridcomplete function is getting called multiple times. With the code below, it gets called twice, but it had been getting called 3 times. Twice is bad enough. After stepping through it multiple times, I don't see what is triggering the second execution of the gridComplete function.
When I hit the debugger at the start of gridComplete, the call stack is virtually identical each time, the only difference being a call to 'L' in the jqgrid:
Anyone have an idea why this is occurring? We are using the free version 4.13, in an ASP.net MVC application.
$(function(){
....
initGrid();
}
function initGrid(){
$gridEl.jqGrid({
xhrFields: {
cors: false
},
url: "/IAConsult/GetWorkFlowIARequests",
postData: {
showAll: showAllVal,
role: role,
IsIAArchitect: userIsIA
},
datatype: "json",
crossDomain: true,
loadonce: true,
mtype: 'GET',
sortable: true,
viewrecords: true,
pager: '#workFlowIAGridPager',
multiselect: true,
rowNum: 50,
autowidth: true,
colModel: [...],
beforeSelectRow: function (rowid, e) {
var $myGrid = $(this),
i = $.jgrid.getCellIndex($(e.target).closest('td')[0]),
cm = $myGrid.jqGrid('getGridParam', 'colModel');
return (cm[i].name === 'cb');
},
jsonReader: {
repeatitems: true,
root: "IAConsultWorkflowRequestsList"
},
beforeSubmitCell: function (rowid, name, value, iRow, iCol) {
return {
gridData: gridData
};
},
serializeCellData: function (postdata) {
return JSON.stringify(postdata);
},
gridComplete: function () {
console.log('grid complete');
let rowIDs = $gridEl.getDataIDs();
let inCompleteFlag = false;
let dataToFilter = $gridEl.jqGrid('getGridParam', 'lastSelectedData').length == 0
? $gridEl.jqGrid("getGridParam", "data")
: $gridEl.jqGrid('getGridParam', 'lastSelectedData');
let $grid = $gridEl, postfilt = "";
let localFilter = $gridEl.jqGrid('getGridParam', 'postData').filters;
let columnNames = columns.split(',');
$('.moreItems').on('click', function () {
$.modalAlert({
body: $(this).data('allitems'),
buttons: {
dismiss: {
caption: 'Close'
}
},
title: 'Design Participants'
});
});
rowCount = $gridEl.getGridParam('records');
gridViewRowCount = rowCount;
let getUniqueNames = function (columnName) {
... };
let buildSearchSelect = function (uniqueNames) {
var values = {};
values[''] = 'All';
$.each(uniqueNames,
function () {
values[this] = this;
});
return values;
};
let setSearchSelect = function (columnName) {
...
};
function getSortOptionsByColName(colName) {
...
}
$grid.jqGrid("filterToolbar",
{ stringResult: true, searchOnEnter: true });
if (localFilter !== "" && localFilter != undefined) {
globalFilter = localFilter;
}
let grid = $gridEl.jqGrid("setGridParam",
{
postData: {
"filters": globalFilter,
showAll: showAllVal,
role: role,
IsIAArchitect: userIsIA
},
search: true,
forceClientSorting: true
});
//grid.trigger("reloadGrid");
//Ending Filter code
for (i = 0; i < columnNames.length; i++) {
var htmlForSelect = '<option value="">All</option>';
var un = getUniqueNames(columnNames[i]);
var $select = $("select[id='gs_workFlowIAGrid_" + columnNames[i] + "']");
for (j = 0; j < un.length; j++) {
val = un[j];
htmlForSelect += '<option value="' + val + '">' + val + '</option>';
}
$select.find('option').remove().end().append(htmlForSelect);
}
debugger;
},
// all grid parameters and additionally the following
loadComplete: function () {
$gridEl.jqGrid('setGridWidth', $(window).width(), true);
$gridEl.setGridWidth(window.innerWidth - 20);
},
height: '100%'
});
I personally almost never use gridComplete callback. It exists in free jqGrid mostly for backwards compatibility. I'd recommend you to read the old answer, which describes differences between gridComplete and loadComplete.
Some additional advices: it's dangerous to register events inside of callbacks (see $('.moreItems').on('click', ...). If you need to make some actions on click inside of grid then I'd recommend you to use beforeSelectRow. Many events, inclusive click event supports event bubbling and non-handled click inside of grid will be bubbled to the parent <table> element. You use already beforeSelectRow callback and e.target gives you full information about clicked element.
I recommend you additionally don't use setGridParam method, which can decrease performance. setGridParam method make by default deep copy of all internals parameters, inclusive arrays like data, which can be large. In the way, changing one small parameter with respect of setGridParam can be expensive. If you need to modify a parameter of jqGrid then you can use getGridParam without additional parameters to get reference to internal object, which contains all jqGrid parameters. After that you can access to read or modify parameters of jqGrid using the parameter object. See the answer for example for small code example.
Also adding the property
loadonce: true
might help.

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.

How to load a .csv file into crossfilter with d3?

I am trying to load a .csv file into crossfilter for further use it with dc.js and d3. However, if the ndx = crossfilter(data_) is not inside d3.csv(..., it does not work. Is it possible to load data using d3 inside a global/outside variable (in this case ndx)?
var ndx;
private method(){
var data_;
d3.csv("samples.csv", function(data){
var format = d3.timeParse("%m-%y");
data.forEach(function(d: any) {
d.date = format(d.date);
});
data_ = d3.csvParse(data);
});
ndx = crossfilter(data_);
}
How can I load it into crossfilter?
Am I obligated to use crossfilter inside the d3.csv(.. call?
Solution:
I made my .csv became a .json and I loaded it 'synchronously'. Observe below.
var ndx;
private method(){
var data_ = (function() {
var json: any = null;
$.ajax({
'async': false,
'global': false,
'url': "samples.json",
'dataType': "json",
'success': function (data:any) {
json = data;
}
});
return json;
})();
ndx = crossfilter(data_);
}
Observe:
'async': false
This happens because the callback function is executed asynchronously, once the data is returned. This means that if you put the charting code outside of the callback, you are going to get the empty array that you defined because no data has been returned yet.

Reploting a JQplot chart using ajax

I have been trying to solve this in different ways, but haven't make it work as expected, I feel it isn't so big deal (I really hope so) but my experience and skills with Ajax and jQuery are kind of limited that's why I am appealing to your expertise!
I am working on a chart similar to the one here http://www.jqplot.com/tests/data-renderers.php. but in my case the json file is generated depending on a value that the user choses from a select box. I am using 2 files and ajax calls to accomplish this:
-AnnualB.html is the file where the select box is located and the chart should be displayed.
-Index.php is the file where the query to the database is made (using the value obtained from the selectbox in AnnualB.html) and the json array is generated.
In AnnualB.html I use the GET method to retrieve the value from the selectbox and send it to Index.php, which generates the json data. Based on that json data the chart has to be created in AnnualB... Here is where my problem comes. The function to generate the chart is working fine and the call to send the select value and return the json is also working (have checked with Firebug), but I know am missing something (but don't know what yet) because I don't manage to pass the json data to the function that generates the chart.
My codes in AnnualB.html goes like this (abbreviating some irrelevant information with ...):
Function to generate the chart (Is working ok if the json data is passed)
function CreateGraph() {
$(document).ready(function(){
var ajaxDataRenderer = function(url, plot) {
var ret = null;
$.ajax({
async: false,
url: url,
dataType:'json',
success: function(data) {
ret = data; }
});
return ret; };
$.jqplot.config.enablePlugins = true;
var jsonurl = "./index.php";
var plotData = ajaxDataRenderer(jsonurl);
var series = ...
plot1 = $.jqplot('Chart1', series,{...}}
Ajax Call (PROBABLY WHERE MY MISTAKE/OMISSION IS)
function LoadGraph(int)
{
if (window.XMLHttpRequest)
{xmlhttp=new XMLHttpRequest();}
else
{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.open("GET","index.php?tasel="+int,true);
xmlhttp.send();
xmlhttp.onreadystatechange=function()
{
CreateGraph(int)
}
}
Select box
<select name="tasel" size="1" onchange="LoadGraph(this.value)">
<option value="">Select accounts type:</option>
<option value="3">Tuloslaskelma</option>
<option value="2">Tasevastattava</option>
<option value="1">Tasevastaava</option>
</select>
The related code in Index.php goes like this (Is working ok when the value of the select box (tasel) is passed)):
$tasel = $_REQUEST['tasel'];
if ($tasel == ...2)
{...}
.
.
.
echo "[",$selite, ",";// These 2 variables create the json array
echo $finaljson, "]";
Thanks in advance for your patience and help!
I realized the answer to this question was simpler than what I was expecting.
Instead of making the whole function LoadGraph(int) ajax call, I just needed to call the tasel value ?tasel="+int in the function to generate the chart like this (which is already doing an ajax call):
function CreateGraph() {
$(document).ready(function(){
var ajaxDataRenderer = function(url, plot) {
var ret = null;
$.ajax({
async: false,
url: url,
dataType:'json',
success: function(data) {
ret = data;
}
});
return ret;
};
$.jqplot.config.enablePlugins = true;
var jsonurl = "./index.php?tasel="+int;
var plotData = ajaxDataRenderer(jsonurl);
var series = ...
plot1 = $.jqplot('Chart1', series,{...}
}
var plot1 = undefined;
var plotOptions = undefined;
function CreateGraph() {
$.ajax({
async: false,
url: "./index.php",
dataType:'json',
success: function(data) {
var series = fn... // Convert your json Data to array
if(plot1 != undefined)
{
plot1.destroy();
}
plot1 = $.jqplot('Chart1', series, plotOptions);
}
});
}
$(function(){
$.jqplot.config.enablePlugins = true;
plotOptions = {...}; // jqPlot options
CreateGraph();
});
Hope this might help you..

Prevent binding execution in getter/setter

In my Ember Controller I am using lazy loading to fetch additional data whenever the configuration is changed. Therefore I want to delay the execution of the binding until all depending data is loaded. I thought returning a false value as the setters result will prevent the binding execution but the binding is still executed. How can I delay the binding execution till loading has finished?
App.ConfigController = Ember.ArrayController.extend({
currentConfiguration: null,
configuration: function(key, value){
if(arguments.length === 1){
return this.get('currentConfiguration');
}
var self = this;
// load additional data
this.loadData(id, function(data){
self.set('currentConfiguration', value);
return value;
})
}
return false;
}.property('currentConfiguration'),
loadData: function(id, resultHandler, faultHandler){
var self = this;
var uri = 'http://foo.com/+id;
$.getJSON(uri)
.success(function(data, status, jqXHR){
resultHandler(data);
})
.error(faultHandler);
},
});
There is no way to prevent binding execution as you wish. The way I would approach this is to have two separate properties; set one, and observe it to update the second, and bind to the second. Something like this:
App.ConfigController = Ember.ArrayController.extend({
configuration: null, // <- set this property
configurationDidChange: function(){
var self = this;
// load additional data
this.loadData(id, function(data){
self.set('configurationData', value);
});
}.observes('configuration'),
configurationData: null <- bind to this property
loadData: function(id, resultHandler, faultHandler){
// ... as you have it
}
});

Resources