A complete package is now available at http://www.jqgrid.com. This was mentioned in a comment on my post about jqgrid, but I thought it was worth a mention here
.
Enjoy !
At work we work with Devexpress components for our windows applications. And I have used some like the pivotgrid and the reporting tool in asp.net websites. They are very powerful, but sometimes hard to use. They have so many options and properties, it’s sometimes hard to find the right one.
Last week the devexpress blog showed a screenshot of their upcoming asp.net mvc grid. It looks suspisciously like jQuery’s tablesorter plugin. What wasn’t revealed though, is how this grid hooks up with the controller, what actions you have to implement etc. So I wonder how they’re going to implement the model ‘hookup’ so to speak.
I am not really anxious to get these kind of tools and components for asp.net mvc, as you can already do that stuff using jquery and your ORM of choice, but you never know, maybe they will come up with something nice, that isn’t overcomplicated and bloated
.
Update : Oops that last part suggest that the devexpress components are bloated, but I didn’t intend it that way. They can be a bit complicated to use though, but their support is always willing to send you a bit of example code if you’re stuck.
Anyway I just hope they come up with something that’s ‘in the spirit’ of MVC, if you know what i mean !
Phil Haack already posted how to use the jQuery grid ‘jqgrid’ on his blog haacked.com. What wasn’t covered in that tutorial was editing, so I thought it would be nice to add that here.
To be honest it gave me a bit of a headache, as I had created a sample table that had the ID column as primary key/autonumber field and jqgrid posts back an ID as well. Mind you, that is the ROWID and not the ID of your record. In a demo app. this can easily go unnoticed as those two are the same when you just add a few records.
After reading up in the docs, and trying to add an extra parameter to the save call (which accidently broke the whole grid), I simply added a hidden column that contains the primary key.
The table I used for this demo was a simple Employees table with an id, first and lastname.
Here’s what the javascript looks like:
jQuery("#list").jqGrid({ url: '/GridDemo/GetGridData', datatype: 'json', mtype: 'GET', colNames: ['IdNr','Id', 'FirstName', 'LastName'], colModel: [ { name: 'IdNr', index: 'IdNr', width: 40, align: 'left', editable: true, editrules: { edithidden: true }, hidden: true }, { name: 'Id', index: 'Id', width: 40, align: 'left', editable: false }, { name: 'FirstName', index: 'FirstName', width: 200, align: 'left', editable: true, edittype: 'text', editoptions: { size: 20, maxlength: 30} }, { name: 'LastName', index: 'LastName', width: 300, align: 'left', editable: true, edittype: 'text', editoptions: { size: 20, maxlength: 30}}], onSelectRow: function(id) { if (id && id !== lastsel2) { jQuery('#list').restoreRow(lastsel2); jQuery('#list').editRow(id, true); lastsel2 = id; } }, editurl: "/GridDemo/GridSave", pager: jQuery('#pager'), rowNum: 10, rowList: [5, 10, 20, 50], sortname: 'Id', sortorder: "desc", viewrecords: true, imgpath: '/content/themes/steel/images', caption: 'Employees' }); });
The IdNr is the hidden field, with the edithidden: true editrules you make sure it gets posted once a row has been edited. I choose for inline editing, there are other forms of editing in the jqgriddocs.
As you can see the editurl is where the data that has been edited will be posted to. That controller action looks like this (using linq2sql, but ofcourse any type of model will do) :
public ActionResult GridSave(int id,int idnr, string firstName, string lastName) { jQueryGridModelDataContext db = new jQueryGridModelDataContext(); Employee e = db.Employees.SingleOrDefault(p => p.ID == idnr); if (!(e == null)) { e.FirstName = firstName; e.LastName = lastName; db.SubmitChanges(); return Content("true"); } else { return Content("false"); } }
And that’s it, your grid now has editing functionality ! Ofcourse some validation is required in the GridSave function. The javascript could use the callback function after saving and check for the true or false that gets returned from the GridSave action.
Let me know in the comments if you have any questions or if I made some sort of horrible mistake
.
This is a simple run through on how to create a fancy search box, that has suggestions underneath the input box, and that also can do autocomplete. For this example I used the Chinook database from codeplex and the Artists table and the autocomplete jquery plug-in that you can find at pengoworks.com.
There are plenty of jQuery plug-ins that do the same thing, I picked this one as it’s very easy to use. As the docs show, the only thing you need to do is hook your input text box up to autocomplete and provide the url where it gets it’s data from. The view looks like this.
<asp:Content ID="headerContent" ContentPlaceHolderID ="headerPlaceHolder" runat ="server"> <script type="text/javascript" src="../../Scripts/jquery.autocomplete.js"></script> <link href="../../Content/jquery.autocomplete.css" rel="stylesheet" type="text/css" /> <script type="text/javascript"> $(document).ready(function() { $("#searchTerm").autocomplete("/Artists/getAjaxResult/"); }); </script> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Look for an artist</h2> <form action="/Artists/Search" method="post" id="searchForm"> <input type="text" name="searchTerm" id="searchTerm" value="" size="10" maxlength ="30" /> <input type ="submit" value="Search" /> </form> <br /> </asp:Content>
As you can see I usually have a container in the header of the master page where javascript and custom css files go. Note that you should use the Url.Content method so you get the proper urls when you deploy this on a ‘real server’.
Now for the backend, we need an action that returns a string of results to the autocomplete call.
public ActionResult getAjaxResult(string q) { string searchResult = string.Empty; var artists = (from a in _db.Artists where a.Name.Contains(q) orderby a.Name select a).Take(10); foreach (Artist a in artists) { searchResult += string.Format("{0}|\r\n", a.Name); } return Content(searchResult); }
By using linq to sql I get the top 10 results from the database from the Artists table and return that as a string to the autocomplete call. The user’s input string is a string called q that is put into the action as a url parameter like this: artists/getAjaxResult/?q=symphony. In order to make that work, you need to add a route to the routes table.
routes.MapRoute( "ArtistSearch", "Artists/Search/{searchTerm}", new { controller = "Artists", action = "Search", searchTerm = "" } ); routes.MapRoute( "ArtistAjaxSearch", "Artists/getAjaxResult/", new { controller = "Artists", action = "getAjaxResult" } );
The first route is used once the user has pressed the submit button, the ArtistAjaxSearch is used for the autocomplete script.
So what happens when the user presses the submit button, whether or not he picked an item from the autocomplete results ?
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Search(string searchTerm) { if (searchTerm == string.Empty) { return View(); } else { // if the search contains only one result return details // otherwise a list var artists = from a in _db.Artists where a.Name.Contains(searchTerm) orderby a.Name select a; if (artists.Count() == 0) { return View("notfound"); } if (artists.Count() > 1) { return View("List", artists); } else { return RedirectToAction("Details", new { id = artists.First().ArtistId }); } } }
As you can see, when a single result is found the details view is shown to the user, by using a redirect to the details method. If there is more than one result, the list of artists is shown. When nothing is found, a simple message is shown. You can of course move the search term to that view as well, to show the user what input he did etc., but that is straightforward stuff.
Another way to do this, would be to insert the artist table as an array and let the autocomplete script use those values for it’s magic, all on the client side. This would be doable as long as that table wouldn’t get too long. If there are 1000s of records, this wouldn’t be a very good solution
.
Hope this helps someone out there
, feel free to add any comments or questions !
Clay made a comment on my previous post and asked how I would use asp.net mvc with jQuery-UI’s tabs. I have slapped together an example project that shows how you could implement the tabs in an asp.net mvc web site. I created an ajax and a regular tab example.
Basically I use a partial view for each tab. This partial view is then also used for the ajax response.
<div id="tabs">
<ul>
<li><a href="#tabs-1">Text 1</a></li>
<li><a href="#tabs-2">Text 2</a></li>
<li><a href="#tabs-3">Text 3</a></li>
</ul>
<div id="tabs-1">
< % Html.RenderPartial("_tab1", Model); %></div>
<div id="tabs-2">
< % Html.RenderPartial("_tab2", Model); %></div>
<div id="tabs-3">
< % Html.RenderPartial("_tab3", Model); %></div>
</div>
There is no database access in the example project, I sort of simulated a database by using a service model that provides the viewmodel that holds the texts that are displayed in the three tabs.
Regarding the ajax controller method, it would of course be better to not get the entire viewmodel in a real world situation and then pass that on to the view, but since this is a brief demo I thought I could get away with this in this example
.
public ActionResult getAjaxTab(int id)
{
string viewName = string.Empty;
TabExample.Services.tabTextService serv = new TabExample.Services.tabTextService();
tabViewModel myModel = serv.getTabViewModel();
switch (id)
{
case 1:
viewName = "_tab1";
break;
case 2:
viewName = "_tab2";
break;
case 3:
viewName = "_tab3";
break;
default:
viewName = "_error";
break;
}
System.Threading.Thread.Sleep(1000);
return PartialView(viewName, myModel);
}
I am using this js function to update the tabs.
function getContentTab(index) {
var url='< %= Url.Content("~/Home/getAjaxTab") %>/' + index;
var targetDiv = "#tabs-" + index;
var ajaxLoading = "<img id='ajax-loader' src='<%= Url.Content("~/Content") %/>/ajax-loader.gif' align='left' height='28' width='28'>";
$(targetDiv).html("
" + ajaxLoading + " Loading...
");
$.get(url,null, function(result) {
$(targetDiv).html(result);
});
}
There is a select event on the tabs that you can hook into as well, I just find it easier to just use the onclick event of the tab div’s.
Hope this is some help to anybody out there, it was fun to create this small project. If anyone has any suggestions, or questions let me know in the comments !
The news of a new jQuery-ui 1.7 release came in through twitter ! The asp.net mvc application I am working on at the moment uses various jquery-ui things, like the accordeon and the datepicker, and some of the icons, but it was a bit messy to actually get it to work. Some of the images were named wrong and the paths didn’t quite work.
The Content and Script folder had become a big mess as well. Luckily with 1.7 that has all passed, I removed the previous version and all images, and inserted the 1.7 smoothness theme. Changed the css and jquery ui calls in the master view and it worked out of the box ! No need to hack around anymore, no image renaming etc… Gratz to the jQuery-ui team for delivering such a nice product.
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> <link href="../../Content/Theme_Red/jquery-ui.css" rel="stylesheet" type="text/css" /> <link href="../../Content/jquery-ui.custom.css" rel="stylesheet" type="text/css" />
If you want to make any changes to the ui, for example I added some padding to the ui-state-error and ui-state-highlight classes, make sure to put them in a seperate file and load them after the included jquery-ui.css file. That way if you change themes or if there is an update, your changes will remain.
There is an odd issue with font-size: and font: in css. I am using the cutline pro 1.4 theme on my eve-blog, and edited the comment section of that theme. IE8 and Google Chrome both showed the fonts real small where as Firefox 3 picked it up the way I intended. Turns out the font: description was not overwritten by font-size in the css file.
Original
body {
font: 62.5% Georgia, "Times New Roman", Times, serif;
}
comment {
font-size: 1.4em;
}
Cleaned up:
body {
font-size: 62.5%;
font-family: Georgia, "Times New Roman", Times, serif;
}
And now it’s fixed
.
Lately I have been trying to get the jquery-ui theme to work nicely with an asp.net mvc site. The hard part was to figure out how to get the paths correctly in the ui.theme.css and ui.all.css files. I ended up just adding the full path in there, only need to search and replace them once I deploy them to the web server. Not a pretty solution, but it works for now.
A nice feature of the jquery-ui theme is that you can use nice little icons, as you can see on the theme gallery page. The trick is to insert a small span that looks like this :
<p class="ui-state-error">
<span class="ui-icon ui-icon-alert" style="float:left;margin-right:2em;"> </span>Error message here !
</p>
<p class="ui-state-highlight">Info box ...</p>
As you might understand these span tags start to clutter up the view code quickly ! So right a little helper you can put in a static class.
public static string InsertIcon(string iconName)
{
return string.Format("<span class=\"ui-icon ui-{0}\" style=\"float: left; margin-right: .2em;\"></span>",iconName);
}
I am also using it in some controllers that return some simple html snippets for jQuery $.ajax methods :
private string createErrorString(string errMsg)
{
string output = "<p class="ui-state-error">";
output += jQueryUiHelper.InsertIcon("icon-alert");
output += errMsg;
output += "</p>";
return output;
}
You can use that in a controller when an error has occured:
return Content(createErrorString ("User " + userName + " does not exist !"));
As you can see, plenty of fun things you can do using the jQuery-ui !
Just updated my theme on my wordpress powered greendale.tk blog. As jQuery is now part of wordpress, I figured I should get rid of the Mootools scripts I was using and rewrite them in jQuery. Nothing against mootools, but the integration is better now with jquery, and I am now more familiar with jQuery as I use it a lot in my asp.net mvc apps. Also it didn’t always play nice with other plugins.
Anyway, I ran into a few issues, and I thought I’d share them here in case anyone else ran into those as well.
How to put the javascript files in your template ? You could ofcourse hardcode them in your headers.php, but that’s not the right way to go. I used to have these references:
<script src="<?php bloginfo(‘template_directory’); ?>/js/eric.js" type="text/javascript"></script>
As long as you use Mootool and not jQuery this works fine, but if you want to include jQuery you should use this to play along nicely with other plugins or scripts that use jQuery:
1: <?php wp_enqueue_script('jquery'); ?>
2: <?php wp_enqueue_script('jquerycolor', '/wp-content/themes/greendale/js/jquery.color.packed.js', array('jquery'),'1.2.6'); ?>
3: <?php wp_enqueue_script('jquerylightbox', '/wp-content/themes/greendale/js/jquery.lightbox.packed.js',array('jquery'),'1.2.6'); ?>
4: <?php wp_enqueue_script('ericjs', '/wp-content/themes/greendale/js/eric-jquery.js',array('jquery'),'1.2.6'); ?>
The wp_enqueue_script command ensured everything is loaded in the right order. If you use the bloginfo command and insert the script yourself, chances are you end up loading your own script before jQuery is loaded ! Which ofcourse results in all sorts of weird errors. The eric-jquery is my own script, the lightbox stuff is for the excellent jQuery lightbox plugin. There are a few lightbox jQuery plugins, but I liked that one the best.
Something else you want to keep in mind. Do not use the $(“#archive_result”) jQuery selectors, but use jQuery(“#archive_result”). Something like this:
1: jQuery(document).ready(function() {
2: jQuery("#recent_content").hide();
3: jQuery("#archive_content").hide();
4:
5: jQuery("#recent_h3").click(function(event) {
6: jQuery("#recent_content").slideToggle();
7: }
8: )
9:
10: jQuery("#archive_h3").click(function(event) {
11: jQuery("#archive_content").slideToggle();
12: }
13: )
14: });
jQuery-ui has some things I might like to add later, for now I just rewrote some of the simple sliding things on the sidebar and the archive page.
Found a video called Asp.net MVC Show me the code through Steve Sandorson’s blog. It’s a presentation on asp.net mvc from a conference called Developer Day. The cool thing about it is that it’s very practical and actually shows you the programming experience (to stay within MS terminology) of programming an Asp.net MVC application. Some things have changed since November, with RC1 now being out, but the general idea is still the same.
http://blog.codeville.net/2009/01/26/video-aspnet-mvc-show-me-the-code/

