How to Display Indirect Data in Jqgrid

How to display indirect data in Jqgrid

I answered already on the closed question before (see here). Nevertheless I decide to answer on your question in detail because the problem which you describe is really very common.

I start with reminding that jqGrid provides formatter: "select" which uses formatoptions.value or editoptions.value to decode ids to texts. The formatter: "select" uses value and optional separator, delimiter and defaultValue properties, but it can't uses editoptions.dataUrl to get required data from the server instead of usage static value. The problem is very easy: processing dataUrl works asynchronous, but during formatting of the column of grid body one don't support delayed filling. So to use formatter: "select" one have to set formatoptions.value or editoptions.value before the server response will be processed by jqGrid.

In the old answer I suggested to extend JSON response returned from the server with additional data for editoptions.value of the columns having formatter: "select". I suggest to set the beforeProcessing. For example one can generate the server response in the following format:

{
"cityMap": {"11": "Chennai", "12": "Mumbai", "13": "Delhi"},
"rows": [
{ "SID": "1", "SNAME": "ABC", "CITY": "11" },
{ "SID": "2", "SNAME": "XYZ", "CITY": "12" },
{ "SID": "3", "SNAME": "ACX", "CITY": "13" },
{ "SID": "4", "SNAME": "KHG", "CITY": "13" },
{ "SID": "5", "SNAME": "ADF", "CITY": "12" },
{ "SID": "6", "SNAME": "KKR", "CITY": "11" }
]
}

and uses the following jqGrid options

colModel: [
{name: "SNAME", width: 250},
{name: "CITY", width: 180, align: "center"}
],
beforeProcessing: function (response) {
var $self = $(this);
$self.jqGrid("setColProp", "CITY", {
formatter: "select",
edittype: "select",
editoptions: {
value: $.isPlainObject(response.cityMap) ? response.cityMap : []
}
});
},
jsonReader: { id: "SID"}

The demo demonstrates the approach. It displays

Sample Image

One can use the same approach to set any column options dynamically. For example one can use

{
"colModelOptions": {
"CITY": {
"formatter": "select",
"edittype": "select",
"editoptions": {
"value": "11:Chennai;13:Delhi;12:Mumbai"
},
"stype": "select",
"searchoptions": {
"sopt": [ "eq", "ne" ],
"value": ":Any;11:Chennai;13:Delhi;12:Mumbai"
}
}
},
"rows": [
{ "SID": "1", "SNAME": "ABC", "CITY": "11" },
{ "SID": "2", "SNAME": "XYZ", "CITY": "12" },
{ "SID": "3", "SNAME": "ACX", "CITY": "13" },
{ "SID": "4", "SNAME": "KHG", "CITY": "13" },
{ "SID": "5", "SNAME": "ADF", "CITY": "12" },
{ "SID": "6", "SNAME": "KKR", "CITY": "11" }
]
}

and the following JavaScript code

var filterToolbarOptions = {defaultSearch: "cn", stringResult: true, searchOperators: true},
removeAnyOption = function ($form) {
var $self = $(this), $selects = $form.find("select.input-elm");
$selects.each(function () {
$(this).find("option[value='']").remove();
});
return true; // for beforeShowSearch only
},
$grid = $("#list");

$.extend($.jgrid.search, {
closeAfterSearch: true,
closeAfterReset: true,
overlay: 0,
recreateForm: true,
closeOnEscape: true,
afterChange: removeAnyOption,
beforeShowSearch: removeAnyOption
});

$grid.jqGrid({
colModel: [
{name: "SNAME", width: 250},
{name: "CITY", width: 180, align: "center"}
],
beforeProcessing: function (response) {
var $self = $(this), options = response.colModelOptions, p,
needRecreateSearchingToolbar = false;
if (options != null) {
for (p in options) {
if (options.hasOwnProperty(p)) {
$self.jqGrid("setColProp", p, options[p]);
if (this.ftoolbar) { // filter toolbar exist
needRecreateSearchingToolbar = true;
}
}
}
if (needRecreateSearchingToolbar) {
$self.jqGrid("destroyFilterToolbar");
$self.jqGrid("filterToolbar", filterToolbarOptions);
}
}
},
jsonReader: { id: "SID"}
});
$grid.jqGrid("navGrid", "#pager", {add: false, edit: false, del: false})
$grid.jqGrid("filterToolbar", filterToolbarOptions);

The demo uses the above code.

We recreate the searching filter if any option are changed dynamically. The way allows implement more flexible solutions. For example the server can detect the language preferences of the client (of the web browser) and return formatting options for numbers, dates and so on based on the options. I'm sure that everyone can suggest other interesting scenarios.

One more remark. If you have too many items in select in (searchoptions.value and editoptions.value) I would recommend you don't use strings instead of objects as the value of searchoptions.value and editoptions.value. It allows you to specify the order of items in the select element.

If you will have too many items in select (for example all cities of your country) then you can consider to use select2 plugin which usage I demonstrate in the answer. It simplify selection of options because it convert select in element which is very close to jQuery UI Autocomplete.

The next demo demonstrate the usage of select2 plugin. If one click on the dropdown arrow of "select" element of the searching toolbar or the searching dialog one get additional input filed which can be used for quick searching. If one starts to type some text in the input box (for example "e" on an example on the picture below) the list of options will be reduced to the options having the typed text as substring:

Sample Image

I personally find such "select-searching" control very practical.

By the way I described in the another answer how to set colNames dynamically. In can be used to manage more information from the server side.

UPDATED: The corresponding controller action Students can be about the following

public class Student {
public long SID { get; set; }
public string SNAME { get; set; }
public long CITY { get; set; }
}
public class City {
public long CID { get; set; }
public string CNAME { get; set; }
}
...
public class HomeController : Controller {
...
public JsonResult Students () {
var students = new List<Student> {
new Student { SID = 1, SNAME = "ABC", CITY = 11 },
new Student { SID = 2, SNAME = "ABC", CITY = 12 },
new Student { SID = 3, SNAME = "ABC", CITY = 13 },
new Student { SID = 4, SNAME = "ABC", CITY = 13 },
new Student { SID = 5, SNAME = "ABC", CITY = 12 },
new Student { SID = 6, SNAME = "ABC", CITY = 11 }
};
var locations = new List<City> {
new City { CID = 11, CNAME = "Chennai"},
new City { CID = 12, CNAME = "Mumbai"},
new City { CID = 13, CNAME = "Delhi"}
};
// sort and concatinate location corresponds to jqGrid editoptions.value format
var sortedLocations = locations.OrderBy(location => location.CNAME);
var sbLocations = new StringBuilder();
foreach (var sortedLocation in sortedLocations) {
sbLocations.Append(sortedLocation.CID);
sbLocations.Append(':');
sbLocations.Append(sortedLocation.CNAME);
sbLocations.Append(';');
}
if (sbLocations.Length > 0)
sbLocations.Length -= 1; // remove last ';'
return Json(new {
colModelOptions = new {
CITY = new {
formatter = "select",
edittype = "select",
editoptions = new {
value = sbLocations.ToString()
},
stype = "select",
searchoptions = new {
sopt = new[] { "eq", "ne" },
value = ":Any;" + sbLocations
}
}
},
rows = students
},
JsonRequestBehavior.AllowGet);
}
}

How to make jqGrid dynamically populate options list based on row data

You don't wrote which version of jqGrid you use currently, but dataUrl can be defined as callback function with (rowid, value, name) parameters, which have to return the URL which you can build dynamically based on the information. The feature exist starting with v4.5.3 (see the line). You can use getCell, getRowData or getLocalRow inside of the callback to get the data from another columns of the row. Thus you can solve your first problem relatively easy.

You second question seems to me absolutely independent from the first one. It's better to separate such questions in different posts to allow the searching engine better to index the information and so to help other people to find it.

There are no simple way how to solve the second problem, but one can sure suggest a solution, but one have to know much more details what you do and how you do. How you start inline editing (do you use inlineNav, formatter: "actions" or you call editRow directly)? Which version of jqGrid (till version 4.7), free jqGrid or Guriddo jqGrid JS you use? How the columns with selects are defined in colModel? Which datatype you use and whether loadonce: true you use? I recommend you to post separate question with the information.

UPDATE: If you have to use old version of jqGrid then you can't generate dataUrl full dynamically, but because you need to add only SpecialValue=100" part to the URL you can follow the trick which I described in many my old answers (the first one was probably here, but the choice on property names which asked the user could be misunderstood). You can use ajaxSelectOptions.data which will define the data parameters of jQuery.ajax request. The problem only that you can define only one ajaxSelectOptions.data property. So you can add the following jqGrid option:

ajaxSelectOptions: {
data: {
SpecialValue: function () {
var rowid = $myGrid.jqGrid("getGridParam", "selrow");
return $myGrid.jqGrid("getCell", rowid, "SpecialValue");
}
}
}

($myGrid is something like $("#grid"))

UPDATED: You used unknown functions getSelectValuesFromJSON, getLookupValuesFromJSON in the updated part of your question. Both of there seems to use synchronous Ajax request which is not good. Moreover you set editoptions.value for only one Field2 instead of setting all selects.

onSelectRow: function (rowid) {
var $myGrid = $(this);
$.ajax({
url: "someUrl",
dataType: "json";
data: {
specialValue: $myGrid.jqGrid("getCell", rowid, "Field1")
},
success: function (data) {
// for example response data have format:
// { "Field2": ["v1", "v2", ...], "Field3": ["v3", "v4", ...] }
var filed, str;
for (filed in data) {
if (data.hasOwnProperty(filed)) {
str = $.map(data[filed], function (item) {
return item + ":" + item
}).join(";");
$myGrid.jqGrid("setColProp", filed, {
editoptions: {
value: str
}
});
}
}
$myGrid.jqGrid("editRow", rowid, true);
}
});
}

Nevertheless the "Solution 2" is more close to what I would recommend you. It's not really important whether to use onSelectRow or beforeSelectRow. You can make asynchronous Ajax request to the server which returns information for all select which you need. After you get the response from the server (inside of success callback) you can set editoptions.value for all selects and only then you can start editRow. In the way you will be sure that editing of the line will use row specific options in all select.

Some additional remarks. I recommend you to verify gridview: true option in the grid. Additionally I suspect that you fill the grid in not full correct way because you have hidden PK column and you use index instead of rowid as the first parameter of beforeSelectRow and onSelectRow. It's very important to understand that the current implementation of jqGrid always assign id attribute on every row (<tr> element) of the grid. So you have to provide id information in every item of input data. If you want to display the id information to the user (and so to have column in colModel with the primary key) then you should just include key: true property in the column definition. For example you can add key: true to the definition of PK column and so you will have rowid (or index in your case) with the same value like PK. It simplify many parts of code. For example jqGrid send id parameter in the editing request to the server. It's practical to have PK in the request. Moreover if you use repeatitems: false format of jsonReader the you can include id: "PK" in the jsonReader instead of having hidden PK column. It informs jqGrid to get rowid from PK. jqGrid will save PK in id attribute of <tr> and you will don't need to have additional <td style="display:none"> with the same information in the grid.

The last remark. I would strictly recommend you to update the retro version jqGrid 3.8.1 to some more recent version, for example to free jqGrid. Even if you would use no features (like Font Awesome for example) you will have performance advantages, and the look of modern web browsers will looks much better. You should understand the jqGrid 3.8.1 was tested with old (and slow jQuery 1.4.2). The version used with Internet Explorer 8 as the latest IE version (IE9 was published later in March 2011) and it's more oriented on IE6/IE7. The look in modern Chrome/Firefox/Safari can be bad. Is it what you want?

free-jqGrid - The numeric value is displayed instead of the text for select cell

You use datatype: "local", but mtype: "POST", url: restURL + "orders/getCustomerOrders" and beforeProcessing options/callbacks, which shows that you do load the data from the server. The column adresa have the following properties

{
name: 'adresa', index:'adresa',label:'Adresa',
formatter:'select',
stype:'select',searchoptions: {sopt: ['eq','ne'],value: getAdresseSelectValues()},
editable:true,edittype:'select',editoptions:{value: getAdresseSelectValues()}
}

with formatter:'select'. It means that the data for adresa column return from the server are numbers like 26 and 27 on your first picture and you want to display there as texts Str. sadasdsd Nr. 34Bl. asdas Etaj asda Ap. asda and Str. asdasdada Nr. 123Bl. 123 Etaj 123 Ap. 123 because of usage editoptions.value defined as

"26:Str. sadasdsd Nr. 34Bl. asdas Etaj asda Ap. asda;27:Str. asdasdada Nr. 123Bl. 123 Etaj 123 Ap. 123"

The problem only is: you want to load the data from the server too. The data will be filled in the grid using formatter:'select'. Thus the value editoptions.value of the column adresa have to be set/modified before the formatter start working.

I suggest you to use beforeProcessing callback, where you process the server response and set/modify editoptions.value of the column adresa. You can use setColProp, for example, for setting editoptions.value. See the answer and this one for more implementation details.

I'd recommend you to use column template additionally (see the old answer) to reduce code duplicates in your code. It will simplify later modification/maintain of your code.

Some additional advices: you use hidden column name: "orderId" with key:true property. The value will be saved in <td> of the column and as the value of id attribute of the row (<tr>). I suggest you to remove the column and to use jsonReader: { id: "orderId" } instead (or localReader: {id: "orderId"} if you load the data not from the server). It will inform jqGrid to assign rowids based on the value of orderId property of input data. Additionally jqGrid will use orderId as the name of rowid during editing (set to editurl) instead of default name id.

You can consider to remove other hidden columns too (custId and createdBy). You need just add the option additionalProperties: ["orderId", "custId", "createdBy"] to inform jqGrid to read the properties from input data. The properties will be seen in custom formatter, rowattr, cellattr and be saved in the local data (accessible by getLocalRow method). In other words, the data will be save in JavaScript objects, but not in the DOM. It makes HTML/DOM cleaner and improve the performance of the page.

jqGrid filterToolbar with local data

All the features are enabled by default if I understand you correctly. The server just have to return all data instead of one page of data to make loadonce: true property work correctly. You need just call filterToolbar after creating the grid. All will work like with local data. You should consider to set sorttype property for correct local sorting and stype and searchoptions for correct filtering of data.

To have "autocomplete" and "excel like filtering options" you need additionally to follow the answer which set autocomplete or stype: "select", searchoptions: { value: ...} properties based on different values of input data. You can do this inside of beforeProcessing callback. The code from the answer use this.jqGrid("getCol", columnName) which get the data from the grid. Instead of that one have access to data returned from the server inside of beforeProcessing callback. So one can scan the data to get the lists with unique values in every column and to set either autocomplete or stype: "select", searchoptions: { value: ...} properties.

UPDATED: I created JSFiddle demo which demonstrates what one can do: http://jsfiddle.net/OlegKi/vgznxru6/1/. It uses the following code (I changed just echo URL to your URL):

$("#grid").jqGrid({
url: "/WebTest/MainAction.do",
datatype: "json",
colNames: ["Label", "Value"],
colModel: [
{name: "label", width: 70, template: "integer" },
{name: "value", width: 200 }
],
loadonce: true,
pager: true,
rowNum: 10,
rowList: [5, 10, "10000:All"],
iconSet: "fontAwesome",
cmTemplate: { autoResizable: true },
shrinkToFit: false,
autoResizing: { compact: true },
beforeProcessing: function (data) {
var labelMap = {}, valueMap = {}, i, item, labels = ":All", values = [],
$self = $(this);

for (i = 0; i < data.length; i++) {
item = data[i];
if (!labelMap[item.label]) {
labelMap[item.label] = true;
labels += ";" + item.label + ":" + item.label;
}
if (!valueMap[item.value]) {
valueMap[item.value] = true;
values.push(item.value);
}
}

$self.jqGrid("setColProp", "label", {
stype: "select",
searchoptions: {
value: labels,
sopt: ["eq"]
}
});
$self.jqGrid("setColProp", "value", {
searchoptions: {
sopt: ["cn"],
dataInit: function (elem) {
$(elem).autocomplete({
source: values,
delay: 0,
minLength: 0,
select: function (event, ui) {
var grid;
$(elem).val(ui.item.value);
if (typeof elem.id === "string" && elem.id.substr(0, 3) === "gs_") {
grid = $self[0];
if ($.isFunction(grid.triggerToolbar)) {
grid.triggerToolbar();
}
} else {
// to refresh the filter
$(elem).trigger("change");
}
}
});
}
}
});

// one should use stringResult:true option additionally because
// datatype: "json" at the moment, but one need use local filtreing later
$self.jqGrid("filterToolbar", {stringResult: true });
}
});

is there anyway to have multiple columns in jqgrid, read the same source data for dropdowns?

Any implementation of what you want will mean some kind of caching of the data for "/Person/GetSelectData". One way which I would prefer myself is the usage of value instead of dataUrl. The list of select values can be included in the main response to the server which fill the grid. In the case the action used in url can returns additional data. You can use the returned data inside of value defined as a function or you can set the value inside of beforeProcessing alternatively. To make my suggestion more clear I explain it on an example.

The first way: usage value as function. One can include the data which you returns typically in "/Person/GetSelectData" inside of main JSON response. For example you can use userdata (or any other extensions of the input data):

{
"rows": [
...
],
"userdata": {
"Persons": "Bill:Bill;Oleg:Oleg;Leora:Leora"
}
}

Then one could use

beforeProcessing: function (data) {
var $self = $(this), userData = data.userdata, persons, selectOptions;
if (userData && userData.Persons) {
persons = userData.Persons;
selectOptions = {
searchoptions: { value: ":All;" + persons }, // for toolbar search
stype: "select",
editoptions: { value: persons },
edittype: "select"
};
$self.jqGrid("setColProp", "Manager", selectOptions);
$self.jqGrid("setColProp", "Delegate", selectOptions);
}
}

By the way one can even use formatter: "select" for "Manager" and "Delegate" columns. It allows to use ids instead of names. For example

"Persons": "3:Bill;1:Oleg;2:Leora"

One should add formatter: "select" to selectOptions too. It allows to use ids 3, 1 and 2 inside of the main data (rows part of JSON data). The standard way with the usage of dataUrl don't allow to use formatter: "select".

I recommend you to read the answer, this one and this one for more information about usage beforeProcessing for dynamic modification of the grid.

jqgrid dynamic columns and data by json

In comments to my answer on your previous answer I described shortly the idea how you can change the column headers based on the data returned from the server. To make all more clear I prepared a demo for you.

I tried to make the demo mostly short and clear, so it has some restrictions:

  • number of columns not changed in different responses from the server
  • the formatters and the width of the column will be not changed in different responses from the server.

All the restrictions can be reduced or removed, but in your case the above restrictions are suffused. Moreover I wanted first describe the main idea of the implementation.

The demo has tree buttons above the grid which allows to reload data from the server, but from different URLs. After clicking on "Load Russian headers" button the headers on the grid will be dynamically changed with the texts from the server response and one will see the following picture

Sample Image

The format of the data is like below:

{
"model": {
"c1": { "label": "Client" },
"c2": { "label": "Date" },
"c3": { "label": "Amount" },
"c4": { "label": "Tax" },
"c5": { "label": "Total" },
"c6": { "label": "Paid" },
"c7": { "label": "Shipped via" },
"c8": { "label": "Notes" }
},
"data": [
{"id": "10", "cell": ["test", "2007-10-01", "200.00", "10.00", "210.00", "true", "TN", "note" ] },
{"id": "20", "cell": ["test2", "2007-10-02", "300.00", "20.00", "320.00", "false", "FE", "note2" ] },
{"id": "30", "cell": ["test3", "2007-09-01", "400.00", "30.00", "430.00", "false", "FE", "note3" ] },
{"id": "40", "cell": ["test4", "2007-10-04", "200.00", "10.00", "210.00", "true", "TN", "note4" ] },
{"id": "50", "cell": ["test5", "2007-10-31", "300.00", "20.00", "320.00", "false", "FE", "note5" ] },
{"id": "60", "cell": ["test6", "2007-09-06", "400.00", "30.00", "430.00", "false", "FE", "note6" ] },
{"id": "70", "cell": ["test7", "2007-10-04", "200.00", "10.00", "210.00", "true", "TN", "note7" ] },
{"id": "80", "cell": ["test8", "2007-10-03", "300.00", "20.00", "320.00", "false", "FE", "note8" ] },
{"id": "90", "cell": ["test9", "2007-09-01", "400.00", "30.00", "430.00", "false", "TN", "note9" ] },
{"id": "100", "cell": ["test10", "2007-09-08", "500.00", "30.00", "530.00", "true", "TN", "note10"] },
{"id": "110", "cell": ["test11", "2007-09-08", "500.00", "30.00", "530.00", "false", "FE", "note11"] },
{"id": "120", "cell": ["test12", "2007-09-10", "500.00", "30.00", "530.00", "false", "FE", "note12"] }
]
}

The most important part of the JavaScript code is

jsonReader: { root: "data" },
beforeProcessing: function (data) {
var $self = $(this), model = data.model, name, $colHeader, $sortingIcons;
if (model) {
for (name in model) {
if (model.hasOwnProperty(name)) {
$colHeader = $("#jqgh_" + $.jgrid.jqID(this.id + "_" + name));
$sortingIcons = $colHeader.find(">span.s-ico");
$colHeader.text(model[name].label);
$colHeader.append($sortingIcons);
}
}
}
}

Full JavaScript used in the demo is below

var $grid = $("#list");
$grid.jqGrid({
url: "DynamicHeaderProperties.json",
datatype: "json",
colModel: [
{ name: "c1", width: 70 },
{ name: "c2", width: 80, align: "center", sorttype: "date",
formatter: "date", formatoptions: {newformat: "m/d/Y"}, datefmt: "m/d/Y"},
{ name: "c3", width: 70, formatter: "number", align: "right",
editrules: {required: true, number: true}, editable: true},
{ name: "c4", width: 60, formatter:"number", align: "right", editable: true,
editrules:{required: true, number: true}},
{ name: "c5", width: 110, formatter: "number", align:"right",
editrules:{required:true,number: true}, editable: true},
{ name: "c6", width: 80, align: "center", editable: true,
formatter:"checkbox",edittype: "checkbox", editoptions: {value: "Yes:No", defaultValue: "Yes"}},
{ name: "c7", width: 110, align: "center", formatter: "select", editable: true,
edittype: "select", editoptions: {value: "FE:FedEx;TN:TNT;IN:Intim", defaultValue: "Intime"}},
{ name: "c8", width: 90, sortable: false, editable:true}
],
rowNum: 10,
rowList: [5,10,20],
pager: "#pager",
gridview: true,
rownumbers: true,
sortname: "c2",
viewrecords: true,
sortorder: "desc",
caption: "Setting coloumn headers dynamicaly",
jsonReader: { root: "data" },
beforeProcessing: function (data) {
var $self = $(this), model = data.model, name, $colHeader, $sortingIcons;
}}


Related Topics



Leave a reply



Submit