I have an ASP.NET MVC application which is executing a search against a products database. I want to display the results in a jqGrid using the TreeGrid module. I don't really need the grid to be AJAX-y because the data is static and it is small enough that it can all be sent to the client at once.
First question: how do I set up jqGrid so that instead of pulling the JSON data from a URL it just looks in a JS variable or something?
Secondly, what is the most appropriate way to get ASP.NET MVC to put JSON data into a JavaScript variable? I already have the List in my controller and just want to somehow get it out into a JS variable after JSON-ing it.
Or am I fighting against the current too much and just accept the AJAX-y way that jqGrid seems to want to work?
Thanks,
~ Justin
Here is how to display a jqGrid tree using a JavaScript function.
$(document).ready(function() {
TreeDemo.setupGrid($("#tree"));
});
TreeDemo = {
data: { A: ["A1", "A2"], B: ["B1", "B2"] },
setupGrid: function(grid) {
grid.jqGrid({
colNames: ['Name'],
colModel: [
{ name: 'Name', index: 'Name', width: "250em" }
],
datatype: TreeDemo.treeData,
loadui: "none",
sortname: 'Number',
treeGrid: true,
treeGridModel: "adjacency",
sortorder: "asc"
})
},
treeData: function(postdata) {
var items = postdata.nodeid ? TreeDemo.data[postdata.nodeid] : TreeDemo.data;
var i = 0;
var rows = new Array();
for (val in items) {
var isLeaf = postdata.nodeid != undefined;
rows[i] = {
Name: val,
Id: val,
level: postdata.nodeid ? 1 : 0,
parent: postdata.nodeid || null,
isLeaf: isLeaf ? "true" : "false",
expanded: "false"
}
i++;
}
$("#tree")[0].addJSONData({
Total: 1,
Page: 1,
Records: 2,
Rows: rows
});
}
};
Note that there are lots of options for how you do this and my example is only one.
The way I would get the JSON into a JS var is to either:
Write a HTML Helper which emits a short script to the page.
Write an action which returns a JavaScriptResult to get the data in a file, if, for some reason, you can't have the data inline.
You create the JSON using the .NET JavaScript serializer. Look at the JsonResult.ExecuteResult in the MVC source code for an example.
See the Data Manipulation page in the jqGrid documentation wiki. There you'll find many ways to feed the data to the grid.
There is also a Table_to_jqGrid plugin that may be an useful option.
Related
I am trying to use a JSON output from the server as the source for my autocomplete function. I read the Autocomplete documentation and it does say that Array of Objects is accepted as a source type. Can someone please let me know where am I going wrong with this?
jq( document ).ready(function() {
jq("body").css({overflow:'hidden'});
jq.getJSON("<?php echo Mage::getBaseUrl() . "setsession/index/getarea"; ?>",
function(data) {
jq( "#autocomplete-1" ).autocomplete({
source: data,
select: function(event, ui) {
alert(ui.item.area_id);
jq("#splash_area").val(ui.item.area_id);
return false;
}
});
}
);
});
This is what I am getting back from the server (JSON encoded):
[{"area_id":"1","area_name":"DLF Phase 1"},{"area_id":"2","area_name":"DLF Phase 2"}]
From the documentation it states an An array of objects with label and value properties: [ { label: "Choice1", value: "value1" }, ... ].
http://api.jqueryui.com/autocomplete/#option-source
Your objects are not defined this way. So for your example something like
[{value:"1",label:"DLF Phase 1"},{value:"2",label:"DLF Phase 2"}]
I have a Select2 that fetches its data remotely, but I would also like to set its value programatically. When trying to change it programatically, it updates the value of the select, and Select2 notices the change, but it doesn't update its label.
https://jsfiddle.net/Glutnix/ut6xLnuq/
$('#set-email-manually').click(function(e) {
e.preventDefault();
// THIS DOESN'T WORK PROPERLY!?
$('#user-email-address') // Select2 select box
.empty()
.append('<option selected value="test#test.com">test#test.com</option>');
$('#user-email-address').trigger('change');
});
I've tried a lot of different things, but I can't get it going. I suspect it might be a bug, so have filed an issue on the project.
reading the docs I think maybe you are setting the options in the wrong way, you may use
data: {}
instead of
data, {}
and set the options included inside {} separated by "," like this:
{
option1: value1,
option2: value2
}
so I have changed this part of your code:
$('#user-email-address').select2('data', {
id: 'test#test.com',
label: 'test#test.com'
});
to:
$('#user-email-address').select2({'data': {
id: 'test#test.com',
label: 'test#test.com'
}
});
and the label is updating now.
updated fiddle
hope it helps.
Edit:
I correct myself, it seems like you can pass the data the way you were doing data,{}
the problem is with the data template..
reading the docs again it seems that the data template should be {id, text} while your ajax result is {id, email}, the set manual section does not work since it tries to return the email from an object of {id, text} with no email. so you either need to change your format selection function to return the text as well instead of email only or remap the ajax result.
I prefer remapping the ajax results and go the standard way since this will make your placeholder work as well which is not working at the moment because the placeholder template is {id,text} also it seems.
so I have changed this part of your code:
processResults: function(data, params) {
var payload = {
results: $.map(data, function(item) {
return { id: item.email, text: item.email };
})
};
return payload;
}
and removed these since they are not needed anymore:
templateResult: function(result) {
return result.email;
},
templateSelection: function(selection) {
return selection.email;
}
updated fiddle: updated fiddle
For me, without AJAX worked like this:
var select = $('.user-email-address');
var option = $('<option></option>').
attr('selected', true).
text(event.target.value).
val(event.target.id);
/* insert the option (which is already 'selected'!) into the select */
option.appendTo(select);
/* Let select2 do whatever it likes with this */
select.trigger('change');
Kevin-Brown on GitHub replied and said:
The issue is that your templating methods are not falling back to text if email is not specified. The data objects being passed in should have the text of the <option> tag in the text property.
It turns out the result parameter to these two methods have more data in them than just the AJAX response!
templateResult: function(result) {
console.log('templateResult', result);
return result.email || result.text;
},
templateSelection: function(selection) {
console.log('templateSelection', selection);
return selection.email || selection.id;
},
Here's the fully functional updated fiddle.
I have gone from incorporating extjs in my original asp.net application which worked when hardcoding any data stores and binding them to the charts/grids. When I tried proxy url calls or even fetching the data from code behind and wrapping in json I still do not get the data into the grid. So I gave up and went with extjs and nodejs and still using mongodb; this worked perfectly but I still have to learn to create a better UI using express/jade etc which is a different project now. But then I came across using MVC with extjs and with a sample project tried the same thing (the sample had hardcoded data) and I cannot for the life of me get it to display the data.
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.state.*'
]);
Ext.onReady(function () {
Ext.QuickTips.init();
// setup the state provider, all state information will be saved to a cookie
Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider'));
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'username', type: 'string' }
]
});
Ext.define('UserStore', {
extend: 'Ext.data.Store',
model: 'User',
autoload: true,
proxy: {
type: 'ajax',
url: '/dashboard.aspx/getDBData',
reader: {
type: 'json',
root: 'users'
},
listeners:
{
exception: function (proxy, response, operation) {
Ext.MessageBox.show(
{
title: 'REMOTE EXCEPTION',
msg: operation.getError(), icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK
});
}
}
}
});
var myStore = Ext.getStore('UserStore');
the url I am including here is the codebehind function that I initially tried which accesses the mongodb and returns json result. Not working.
Now from the extjs node.js application I have results coming into localhost:3000/userlist which returns a list from mongodb and displays it as follows:
extends layout
block content
h1.
User List
u1
each user, i in userlist
li
a(href="mailto:#{user.email}")= user.username
Now would it be possible to use the same server and call the base url and then change the route.js file to return the mongodb json result or call the mongodb localhost:27017 and get a result. Really confused here
exports.index = function(db) {
return function(req, res) {
var collection = db.get('usercollection');
collection.find({},{}, function(e,docs){
res.render('userlist', {
"userlist" : docs
});
});
};
};
EDIT:
First thing I realized from asp.net perspective was that I was not calling a webservice just a codebehind method. Any comments will still be appreciated.
EDIT 2:
{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}
You have identified a root in your store as 'users'
reader: {
type: 'json',
root: 'users'
},
But there is no root in your returned json such as:
{"users":[{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}]}
I'm using select2 and I want to set a custom field for the text property of the rendered items without
replacing standard behavior (marking and such)
pushin all my array into a new one with the text field on it
ps: i just want to a render many select2 items that doesn't have a text field
Basically if you see this jsbin you will see something like this
$("#e10_3").select2({
data:{ results: data, text: function(item) { return item.tag; } },
formatSelection: format,
formatResult: format
});
But if I delete the custom formatSelection and formatResult parameters of select2 I loose my hability to use a different field for text.
I suggest this approach
$("#e10_3").select2({
data:{
results: function (data) {
return {
results: $.map(data, function (item) {
return {
text: item.tag,
id: item.id
...
}
})
};
}
},
formatSelection: format,
formatResult: format
});
As previous answer this solution DOES create a new array, but it seems to be a better approach considering readability. Before passing data you should modify it. You can see this in official docs
var data = $.map(yourArrayData, function (obj) {
obj.text = obj.text || obj.name; // desired field
return obj;
});
The only other option is to prepare data with text property matched to desired property from the very beginning.
UPDATE (added an example)
$('your-select2-el').select2
data: $.map(yourArrayData, (obj) ->
obj.text = obj.your_custom_field_name # obj.title or obj.name etc.
obj
)
See docs in the link I provided before and this one
I've got a few jqGrids on my ASP.NET MVC 3 application that have a number of columns. I added the following to the column definitions to default some columns to be hidden:
colModel: [
{ name: 'IceCreamID', hidden: true},
{ name: 'RecipeID', hidden: true }
and this works nicely. Those columns aren't visible on my grid.
Then I added this to implement the column chooser:
var grid = $('#icecreamGrid');
grid.jqGrid('navButtonAdd', '#icecreamPager',
{ caption: "Columns", buttonicon: "ui-icon-calculator",
title: "Choose Columns",
onClickButton: function() {
grid.jqGrid('columnChooser');
}
});
Great, brings up the column chooser now. I then added the following to columns I never wanted to show up in the column chooser:
colModel: [
{ name: 'IceCreamID', hidden: true, hidedlg: true},
So I can now hide/show columns just fine. Now, how would you persist this information? DB? As a cookie? Other way? Is there a preferred way to store this sort of information that is really a user preference rather than something related to the data itself?
More Info
Based on Oleg's comment below, I want to provide a little more information.
The point here is that I've got grids with 10-15 columns which could be display based on the user's preference. For a simple example, one of my grid's has the following 9 columns:
IceCream|ShortName|HasNuts|SugarAdded|LimitedRun|PromoItem|Facility|FirstRun|LastRun
Users can hide/show any of these 9 columns based on their personal preferences.
What I want to do is provide a way to persist which columns a particular user wants to see so that s/he doesn't have to re-choose those columns to view each time the page with that grid is shown.
I found you question very interesting. The question about saving the user state of grid are interesting in many cases. There are some interesting answers on such problems which uses cookie (see here for example).
In my opinion saving of the grid state in database on the server or in the localStorage is better way as the usage of cookie. The best way depends on the project's requirements in which you use it. For example the usage of the database storage on the server allows you to implement roaming state of the grid. If you use the localStorage instead of cookies the user preferences will be lost if the user goes to another computer or just if the user will use another web browser on the same computer.
Another problem with the grid state is the maintenance. The information about the columns of the grid you hold typically in the JavaScript or HTML files and not in the database. In the case the both sources can be not synchronous on the changes in the grid. Different scenarios of the update problem could you easy imagine. Nevertheless the advantages of user's preferences so large in some scenarios that the problems with small disadvantages are not so important and can be solved relatively easy.
So I'll spend some time to implement two demos which shows how it can be implemented. I used localStorage in my demos because of many reasons. I mention only two from there:
Cookies is the way which send permanently different information to or from the server which is not really requited. It increases the size of HTTP header and decreases the performance of the web site (see here for example).
Cookies have very hard restrictions. Corresponds to the section 6.3 of rfc2109 or 6.1 of rfc6265: At least 4096 bytes per cookie, at least 50 cookies per domain (20 in rfc2109), at least 3000 cookies total (300 in rfc2109). So the cookies one can't use to save too many information. For example if you would save state of every grid of every your web page you can quickly achieve the limits.
On the other side localStorage are supported by all modern browsers and will be supported in Internet Explorer starting with IE8 (see here). The localStorage will be automatically saved per origins (like a1.example.com, a2.example.com, a3.example.com, etc) and has arbitrary limit of 5 MB per origin (see here). So if you use the space carefully you will far from the any limits.
So I used in my demos the localStorage. I should additionally mention that there are some plugins like jStorage which use localStorage if it's supported by the browser and use another storage, but the same interface for you in case of old browsers like IE6/IE7. In the case you has only less size of storage: 128 kB instead of 5 MB, but it's better as 4K which one has for cookies (see here).
Now about the implementation. I creates two demos: this and it's extended version: this.
In the first demo the following states of grid will be saved and automatically restored on the page reload (F5 in the most web browsers):
which column are hidden
the order of columns
the width of every column
the name of the column by which the grid will be sorted and the sort direction
the current page number
the current filter of the grid and the flag whether the filter are applied. I used multipleSearch: true setting in the grid.
In the same way one can extend (or reduce) the list of options which are the part of the saved grid state.
The most important parts of the code from the demo you will find below:
var $grid = $("#list"),
saveObjectInLocalStorage = function (storageItemName, object) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.setItem(storageItemName, JSON.stringify(object));
}
},
removeObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.removeItem(storageItemName);
}
},
getObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
return $.parseJSON(window.localStorage.getItem(storageItemName));
}
},
myColumnStateName = 'ColumnChooserAndLocalStorage.colState',
saveColumnState = function (perm) {
var colModel = this.jqGrid('getGridParam', 'colModel'), i, l = colModel.length, colItem, cmName,
postData = this.jqGrid('getGridParam', 'postData'),
columnsState = {
search: this.jqGrid('getGridParam', 'search'),
page: this.jqGrid('getGridParam', 'page'),
sortname: this.jqGrid('getGridParam', 'sortname'),
sortorder: this.jqGrid('getGridParam', 'sortorder'),
permutation: perm,
colStates: {}
},
colStates = columnsState.colStates;
if (typeof (postData.filters) !== 'undefined') {
columnsState.filters = postData.filters;
}
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colStates[cmName] = {
width: colItem.width,
hidden: colItem.hidden
};
}
}
saveObjectInLocalStorage(myColumnStateName, columnsState);
},
myColumnsState,
isColState,
restoreColumnState = function (colModel) {
var colItem, i, l = colModel.length, colStates, cmName,
columnsState = getObjectFromLocalStorage(myColumnStateName);
if (columnsState) {
colStates = columnsState.colStates;
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colModel[i] = $.extend(true, {}, colModel[i], colStates[cmName]);
}
}
}
return columnsState;
},
firstLoad = true;
myColumnsState = restoreColumnState(cm);
isColState = typeof (myColumnsState) !== 'undefined' && myColumnsState !== null;
$grid.jqGrid({
// ... other options
page: isColState ? myColumnsState.page : 1,
search: isColState ? myColumnsState.search : false,
postData: isColState ? { filters: myColumnsState.filters } : {},
sortname: isColState ? myColumnsState.sortname : 'invdate',
sortorder: isColState ? myColumnsState.sortorder : 'desc',
loadComplete: function () {
if (firstLoad) {
firstLoad = false;
if (isColState) {
$(this).jqGrid("remapColumns", myColumnsState.permutation, true);
}
}
saveColumnState.call($(this), this.p.remapColumns);
}
});
$grid.jqGrid('navButtonAdd', '#pager', {
caption: "",
buttonicon: "ui-icon-calculator",
title: "choose columns",
onClickButton: function () {
$(this).jqGrid('columnChooser', {
done: function (perm) {
if (perm) {
this.jqGrid("remapColumns", perm, true);
saveColumnState.call(this, perm);
}
}
});
}
});
$grid.jqGrid('navButtonAdd', '#pager', {
caption: "",
buttonicon: "ui-icon-closethick",
title: "clear saved grid's settings",
onClickButton: function () {
removeObjectFromLocalStorage(myColumnStateName);
}
});
Be carefully to define myColumnStateName (the value `'ColumnChooserAndLocalStorage.colState'``) in the demo) to different values on the different pages.
The second demo is the extension of the first one using the technique from my old answer to your another question. The demo use the searching toolbar and synchronize additionally information between the advanced searching form and the searching toolbar.
UPDATED: The next answer contains extended version of the code included above. It shows how to persist the selected rows (or row) additionally. Another answer shows how to persist the list of expanded nodes of the tree grid and expand the nodes on the relaoding of the page.