Building multiple column displays in MVC

By James at January 12, 2011 16:34
Filed Under: ASP.NET

Looping through a list of objects to display a table of the items is easy in MVC. All you need to do is:

   1: <table>
   2: <%foreach (var item in friendList){%>
   3:   <tr>
   4:     <td><%item.Friend.FullName %></td>
   5:   </tr>
   6: <%}%>
   7: </table>

But what if your display calls for a multi-column layout, say with 4 columns and an unknown number of rows? I have pondered this over and over in many of my projects and have come up with the following code to do this. I have a helper file which my Controller calls to build a string of div tags to display the results. It may look complicated but I will go over the details below.

   1: public string GetAvailableFriends(int memberId)
   2: {
   3:   var list = _repo.List<Member>().Where(m => m.MemberContact.Equals(true))
   4:              .OrderBy(m => m.FirstName).ToList();
   5:   var memberFriends = _repo.List<MemberFriend>()
   6:             .Where(m => m.Member.Id.Equals(memberId)).ToList();
   7:             
   8:   var currentFriends = memberFriends.Select(f => f.Friend).ToList();
   9:  
  10:   var availableFriendList = (from friend in list
  11:                              where !currentFriends.Contains(friend)
  12:                              select new MemberFriends
  13:                             {
  14:                              FirstName = friend.FirstName, 
  15:                              LastName = friend.LastName,
  16:                              FriendId = friend.Id, 
  17:                              FullName = friend.FirstName + " " + friend.LastName,
  18:                              Avatar = friend.Avatar
  19:                              }).ToList();
  20:  
  21:   decimal listCount = availableFriendList.Count;
  22:   var listLimit = Math.Floor(listCount / 4) + 2;
  23:   if (listLimit == 0) listLimit = 1;
  24:   var addedFriends = 0;
  25:             
  26:   var friendTable = new StringBuilder("<div class='availFriends'>");
  27:             
  28:   for (var i = 0; i < listLimit; i++)
  29:   {
  30:      if (addedFriends >= listCount) continue; // we're all done, no need to continue
  31:  
  32:      friendTable.Append(i < listLimit - 1 ? "<div class='row availFriendRow'>" : "<div class='row last'>");
  33:  
  34:      //first column
  35:      if (addedFriends < listCount)
  36:      {
  37:        var friend1 = availableFriendList.ElementAt(addedFriends);
  38:        var avatarSource = _photoHelpers.GetAvatarThumbSource(friend1);
  39:        FriendTableAppend(friendTable, friend1, avatarSource);
  40:        addedFriends++;
  41:      }
  42:  
  43:      //second column
  44:      if (addedFriends < listCount)
  45:      {
  46:        var friend2 = availableFriendList.ElementAt(addedFriends);
  47:        var avatarSource = _photoHelpers.GetAvatarThumbSource(friend2);
  48:        FriendTableAppend(friendTable, friend2, avatarSource);
  49:        addedFriends++;
  50:      }
  51:      
  52:      //third column
  53:      if (addedFriends < listCount)
  54:      {
  55:        var friend3 = availableFriendList.ElementAt(addedFriends);
  56:        var avatarSource = _photoHelpers.GetAvatarThumbSource(friend3);
  57:        FriendTableAppend(friendTable, friend3, avatarSource);
  58:        addedFriends++;
  59:      }
  60:      
  61:      //fourth column
  62:      if (addedFriends < listCount)
  63:      {
  64:        var friend4 = availableFriendList.ElementAt(addedFriends);
  65:        var avatarSource = _photoHelpers.GetAvatarThumbSource(friend4);
  66:        FriendTableAppend(friendTable, friend4, avatarSource);
  67:        addedFriends++;
  68:      }
  69:  
  70:      friendTable.Append("</div>");
  71:   }
  72:   friendTable.Append("<div class='sendFriendRequest sendFriendRequestDisabled'>Send Friend Request</div>");
  73:   friendTable.Append("</div>");
  74:   return friendTable.ToString();
  75: }
  76:  
  77: private void FriendTableAppend(StringBuilder friendTable, MemberFriends friend, string avatarSource)
  78: {
  79:    friendTable.Append("<div class='col col225'>");
  80:    friendTable.Append("<div class='availFriendHolder'>");
  81:    friendTable.Append("<div class='availFriendCheckBox chkGraphic chkUnChecked' fxn='");
  82:    friendTable.Append(friend.FriendId + "'><input type='hidden' name='");
  83:    friendTable.Append(friend.FriendId + "'/></div>");
  84:    friendTable.Append("<div class='availFriendAvatar'><img src='" + avatarSource + "' /></div>");
  85:    friendTable.Append("<div class='availFriendName'>" + friend.FullName + "</div>");
  86:    if(FriendRequestPending(friend.FriendId))
  87:    {
  88:        friendTable.Append("<div class='friendRequestPending'>Friend Request Pending</div>");
  89:    }
  90:    friendTable.Append("</div>");
  91:    friendTable.Append("</div>");
  92: }
  93:  
  94: public bool FriendRequestPending(int friendId)
  95: {
  96:     var approved = false;
  97:     var memberFriend = _repo.List<MemberFriend>()
  98:         .Where(m => m.FriendId.Equals(friendId))
  99:         .ToList().FirstOrDefault();
 100:     if (memberFriend != null)
 101:         approved = memberFriend.Approved;
 102:     return approved;
 103: }

 

  1. The first thing to do is set the variable listCount to how many elements are in the list – line 21
  2. Set a variable, “listLimit” to listCount divided by the number of columns you want, in this case 4 – line 22
  3. If the result comes back as 0 then set it to 1 – line 23
  4. Set a variable “addedFriends” to 0 – line 24
  5. Start a loop – line 28
  6. Inside the loop, grab the element in the list that is indexed at “addedFriends” – lines 37, 46, 55, 64
  7. Call the helper method FriendTableAppend to continue building out the string.
  8. At the end of the loop, finish up the string, then return it.

 

Easy, peasy, nice and cheesy.

Happy Programming from ComponentOne

James

Inland Empire .NET User’s Group Meeting–January 11, 2011-Jeremy Clark

By James at January 12, 2011 11:06
Filed Under: Inland Empire .NET UG

Jeremy Clark from BiTKOO came to present, “Learn to Love Lambdas” to the IEDOTNETUG on Tuesday January 11, 2011. It was a great presentation and really opened the doors on learning how lambdas work and how to best use them.

Jeremy works for BiTKOO, a company specializing in Identity and Access Management. After hours, he puts together demos and technical articles for JeremyBytes.com, a website focusing on .NET technologies.

Attendance: 25

Raffle Prizes: 30

-James

Uploading photos with MVC

By James at December 22, 2010 13:49
Filed Under:

There are many articles online talking about MVC, but not many showing how to upload files. In a project I’m working on I need to have my users upload photographs and wanted them to be able to preview the photo before submitting it. Here’s a screen shot of what I’m talking about.

7318.image_677BC867

The application allows the user to select a photo, which is then previewed. The user can then add a caption and description of the photo. During this first round the Save button is bound to the following jQuery script:

   1: function uploadMemberPhoto(elem) {
   2:     var $elem = $(elem);    
   3:     $("div.memberPhotoUploader").fadeIn(500);
   4:     var $form = $elem.parents("form");
   5:     $form.attr("action", "/MemberPhotos/Uploaded");
   6:     $elem.fadeOut(500, function () { $form[0].submit(); });
   7: }

which sets the action of the form to “/MemberPhotos/Uploaded”, then submits the form. The Controller action “Uploaded” handles the processing of the image.

   1: public ActionResult Uploaded(HttpPostedFileBase file, FormCollection formCollection)
   2: {
   3:   var memberPhoto = _photoHelpers.UploadMemberPhoto(file, formCollection);  
   4:   return View("UploadedPhoto");
   5: }

_photoHelpers is a helper class which does the processing of the image

   1: public MemberPhoto UploadMemberPhoto(HttpPostedFileBase file, FormCollection collection)
   2: {
   3:   var memberPhoto = new MemberPhoto();
   4:   try
   5:   {
   6:      if(file.ContentLength > 0)
   7:      {
   8:       var member = _repo.Get<Member>(int.Parse(collection["memberId"]));
   9:       var photoCaption = collection["memberPhotoCaption"];
  10:       var photoDescription = collection["memberPhotoDescription"];                    
  11:       var serverPath = HttpContext.Current.Server.MapPath(
  12:              ConfigurationManager.AppSettings["memberAvatarPath"]);
  13:       var memberFolder = GetMemberImageFolder(member);
  14:       var imagePath = serverPath + memberFolder;
  15:  
  16:       if (!Directory.Exists(imagePath))
  17:           Directory.CreateDirectory(imagePath);
  18:  
  19:       bool success = ImageHelpers.ProcessMemberPhotoUpload(file, imagePath);
  20:       if(success) // insert the record
  21:       {
  22:           memberPhoto.MemberId = member.Id;
  23:           memberPhoto.PhotoUrl = file.FileName;
  24:           memberPhoto.PhotoCaption = photoCaption;
  25:           memberPhoto.PhotoDescription = photoDescription;
  26:           memberPhoto.IsApproved = false;
  27:           memberPhoto.IsVisible = true;
  28:           memberPhoto.CreateBy = member.Email;
  29:           memberPhoto.CreateDate = DateTime.Now;
  30:           memberPhoto.ModifyBy = member.Email;
  31:           memberPhoto.ModifyDate = DateTime.Now;
  32:           memberPhoto.Mode = ImageHelpers.ImageMode(file);
  33:           _repo.Create(memberPhoto);
  34:        }
  35:     }
  36:  }
  37:   catch (Exception ex)
  38:   {
  39:     memberPhoto = null;
  40:   }
  41:   return memberPhoto;
  42: }
   1: public bool ProcessMemberPhotoUpload(HttpPostedFileBase img, string fPath)
   2: {
   3:   fPath = fPath.Replace("\\\\", "\\");
   4:   var resizedImage = ResizeImage(img, new Size(640, 480));
   5:   var status = SaveImage(resizedImage, fPath, img.FileName);
   6:  
   7:   var thumbNail = ResizeImage(img, new Size(102, 77));
   8:   status = SaveImage(thumbNail, fPath, "thmb_" + img.FileName);
   9:  
  10:   var largeThumb = ResizeImage(img, new Size(400, 600));
  11:   status = SaveImage(largeThumb, fPath, "lgthmb_" + img.FileName);
  12:   return status;
  13: }

The rest of the methods used in the helper class are here

   1: private static Image ResizeImage(HttpPostedFileBase img, Size size)
   2: {
   3:   var oImage = Image.FromStream(img.InputStream);
   4:   var sWidth = oImage.Width;
   5:   var sHeight = oImage.Height;
   6:             
   7:   if(size.Width >= oImage.Width && size.Height >= oImage.Height)
   8:       return (Image) oImage;
   9:  
  10:   float nPercent = 0;
  11:   float nPercentW = 0;
  12:   float nPercentH = 0;
  13:  
  14:   nPercentW = ((float) size.Width/(float) sWidth);
  15:   nPercentH = ((float) size.Height/(float) sHeight);
  16:  
  17:   if (nPercentH < nPercentW)
  18:      nPercent = nPercentH;
  19:   else
  20:      nPercent = nPercentW;
  21:  
  22:   int dWidth = (int) (sWidth*nPercent);
  23:   int dHeight = (int) (sHeight*nPercent);
  24:  
  25:   Bitmap b = new Bitmap(dWidth, dHeight);
  26:   Graphics g = Graphics.FromImage((Image) b);
  27:   g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  28:   g.DrawImage(oImage, 0, 0, dWidth, dHeight);
  29:   g.Dispose();
  30:   return (Image) b;
  31:  }
  32:  
  33:  private static bool SaveImage(Image img, string savePath, string fileName)
  34:  {
  35:    var status = true;
  36:    try
  37:    {
  38:       var newPath = new StringBuilder(savePath).Append(fileName);
  39:       File.Delete(newPath.ToString());
  40:       img.Save(newPath.ToString(), img.RawFormat);
  41:    }
  42:    catch (Exception)
  43:    {
  44:      status = false;
  45:    }
  46:    return status;
  47: }
  48:  
  49: public string ImageMode(HttpPostedFileBase img)
  50: {
  51:   var oImage = Image.FromStream(img.InputStream);
  52:   var mode = "landscape";
  53:   if (oImage.Width < oImage.Height) mode = "portrait";
  54:      oImage.Dispose();
  55:   return mode;
  56: }

In this particular application, all photos uploaded need to be approved, so after the photo is uploaded, the page refreshes, keeping the original form action of “/MemberPhotos/UpdatePhoto”

8688.image_61FD31F6

   1: public ActionResult UpdatePhoto(MemberPhoto photo)
   2: {
   3:    photo.ModifyBy = User.Identity.Name;
   4:    photo.ModifyDate = DateTime.Now;
   5:    _repo.Edit(photo);
   6: }

You may be wondering how the image is previewed. I found this article at, http: //weblogs.asp.net/imranbaloch/archive/2010/04/03/image-preview-in-asp-net-mvc.aspx and modified it a bit for my needs. This bit of javascript works like a champ.

   1: function ChangeImage(fileId, imageId) {
   2:     $("div.memberPhotoUploader").fadeIn(500);
   3:     $("#form1").ajaxSubmit({ success: function (responseText) {
   4:         if (isImage(responseText)) {
   5:             var d = new Date();
   6:             $(imageId)[0].src = "/ImagePreview/ImageLoad?a=" + d.getTime();
   7:             $(imageId).show();
   8:             $("input#uploadButton").removeClass("btnSaveDisabled")
   9:               .addClass("btnSaveEnabled").removeAttr("disabled").click(function () { 
  10:                 uploadMemberPhoto(this); 
  11:           }); ;
  12:         } else {
  13:             $(imageId).hide();
  14:             $("div.imageTypeNotice").show();
  15:         }
  16:         $("div.memberPhotoUploader").fadeOut(500);
  17:     }
  18:     });
  19: }

Happy programming from ComponentOne

James

Easy control styling with Foxy

By James at December 20, 2010 19:28
Filed Under: Foxy, Visual Style Designer, Design

Using third party controls like ComponentOne’s tools makes building your websites faster and easier. And, with all the themes that the C1 tools have, chances are you can find one which blends nicely. But if you can’t then what do you do? You really don’t want to go digging through all the CSS and graphics files to make a few changes, nor do you want to make your site match the theme you’ve chosen. Well fret no more, along comes Foxy.

5417.image_51FE523C

C1’s Foxy allows you to edit a theme from Studio for ASP.NET, or even create your own theme to use with the Studio for ASP.NET components. You can choose to edit just a few controls or the entire theme’s suite, drilling down as far as you want to. More than likely all you really need to do is just change the colors slightly, and this is where Foxy really makes it easy for you by allowing you to change the HLS properties of the theme. Here’s how to do it.

1. Open Foxy. It will be in your Start Menu at ComponentOne, Studio for ASP.NET AJAX, Style Designer, Foxy for ASP.NET

7571.image_49DF5247

2. I have found that “ArticFox” is the best one to modify via the Colorize feature as it is the most bland so all the elements will look good when colorized. From the VisualStyles window, select a ArticFox and drag it down to the Custom VisualStyles window

8231.image_155E7FFF

3. Expand the ArcticFox-Custom1 node, then double-click one of the controls. I am going to continue this using the C1Calendar. If you plan to only use a few controls in your project, select, then delete, the controls in this panel you won’t be using. Otherwise when you import into your project all of them will go across as well.

8463.image_251E1503

4. Double-clicking the control, opens the control in the Foxy designer surface, showing the hierarchy of the control’s elements, the control itself and the CSS for the control

2045.SNAGHTMLde2d4c4_1843BEE5

5. For now, just select the C1Calendar node in the upper left corner of the screen, then click on the Colorize button in the tool bar towards the upper right.

0451.image_1475F448

6. This brings up the Hue/Saturation dialog window

1057.SNAGHTMLde65d16_250DEF36

7. Click on the Colorize check box. You can then choose a color, or make changes to the current color with the Hue, Saturation and Lightness sliders. I’ve selected the teal color, (4 over, 3 down).

3005.image_716582D7

8. In the Hue/Saturation dialog window, start playing with the sliders to colorize the theme. You will see the changes in real time.

3201.image26_7A720E58

9. Setting the values to Hue: 124, Saturation: 35, Lightness: –46 makes a nice Olive color. Click on Apply and you will see your changes in the designer pane.

3288.image_2D619EC0

What’s really nice about the Colorize option is that it takes care of the graphics as well, giving them the same colors as the HTML elements.

The C1Calendar control, as most of the other C1 controls are set to inherit the font family of your own style sheet. However they may be times when you want the control to have a bit different font – a little bigger, bold perhaps, maybe a different color – and this is another area where Foxy makes it easy.

1. In the left hand control panel, select the “Control” node, just after the parent node, “C1Calendar”

2. In the Ribbon Font bar, select a font family from the drop down. The changes will be made in real time so you can play around with the different settings to get just the look you want.

5672.image_302753B3

When you get all your changes made, click on the Save button. So, we made this nice theme for our calendar, now how do we get it into our project?

1. In your ASP.NET Web Application Solution Explorer, right click on the project name and select Add, New Folder. Name the new folder, “VisualStyles”, then right click on the new folder and select Add, New Folder, naming this folder the name of the theme you just created. On this new folder right click again and select Add, New Folder, naming this one C1Calendar.

5556.image_039E23CD

2. Add the custom css file you created above by right clicking on C1Calendar then selecting Add, Existing Item. Navigate to where Foxy saves your files on your computer, typically “C:\Users\<UserName>\Documents\C1FoxyVSD\<Custom Theme Name>\<Control Name>. In this case the folder I am looking for is “C:\Users\James\Documents\C1FoxyVSD\ArcticFox-Custom1\C1Calendar\styles.css”. Select the file, then click on Add.

3. We need to add the images for the Calendar control as well, so right click on the C1Calendar folder in the Solution Explorer and select Add, New Folder, naming it “Images”. Right click on the new “Images” folder and select Add, Existing Item, then navigate to the same folder as you went to in Step 2 above, “C:\Users\James\Documents\C1FoxyVSD\ArcticFox-Custom1\C1Calendar\Images”. Select the two files, “calendar.png” and “calendar-repeater.png”, then click on Add.

4. Add a C1 Calendar control to your page, by dragging it from the Toolbox to the aspx page in Visual Studio, then click on the Design tab in the lower left corner of Visual Studio.

5. Click the Calendar control’s Task button to bring up the Task dialog

4774.image_33E4F883

6. Change the VisualStylePath to ~/VisualStyles, this is the folder you created in step 1

7. Uncheck the UseEmbeddedVisualStyles check box

8. Click in the VisualStyle drop down and your Custom style will be present. Select it

6136.image_2AA8BD42

9. Run your application and you will see the calendar rendered with the new Visual Style

2806.before_6855A4FE 7840.after_0653B2F3
Before styling with Foxy After styling with Foxy

In this article we talked about how easy it is to use ComponentOne’s controls. We also talked about how using the Visual Style designer, Foxy, can make it really simple to make small changes to the controls to add your own colors and emphasis. Foxy is a really great tool, that can make your development even more fun and productive.

Happy Programming,

James

jQuery and MVC–Part 4, now with Wijmo!

By James at December 17, 2010 11:04
Filed Under: JavaScript, jQuery, jQuery Plugins, Wijmo

In part three of this series I talked about how to extend jQuery with plugins, went through the steps of writing a plugin, and then demonstrated how to use it in a standard HTML page. I showed you a popular Table Sorter plugin and showed how to initialize it with different options. In this last article I will show you one more plugin, the Wijmo Grid, and how easy it easy to implement it’s different options and features. I will also show you some of the cool things that Wijmo can do to help spruce up your web forms and pages.

Going back to the Student Course Admin form I’ve been using for these articles, I am generating a table of student data in an ASP.NET MVC app.

7563.image_281CC156

One thing I did add since the last article was the ability to filter the students by their first or last name.

4186.image_2BDA6626

3666.image_43919D8C

This was done by adding some JavaScript and an Ajax call which gets a new set of data from the server, then replaces the original table with the new table.

   1: function getStudentsByName(elem) {
   2:     $("img.ajaxLoaderFName").fadeOut(500);
   3:     $("img.ajaxLoaderLName").fadeOut(500);
   4:     var $elem = $(elem);
   5:     var url = "";
   6:     var ajaxLoader;
   7:     if ($elem.attr("id") == "txtFilterFName") {
   8:         url = "/Home/GetStudentsByFirstName/" + $elem.val();
   9:         ajaxLoader = $("img.ajaxLoaderFName");
  10:     }
  11:     if ($elem.attr("id") == "txtFilterLName") {
  12:         url = "/Home/GetStudentsByLastName/" + $elem.val();
  13:         ajaxLoader = $("img.ajaxLoaderLName");
  14:     }
  15:     
  16:     ajaxLoader.fadeIn(500);
  17:     $.ajax({
  18:         url: url,
  19:         type: "GET",
  20:         success: function (result) {
  21:             var holder = $("table.studentTable").parent("div.row");
  22:             $("table.studentTable").remove();
  23:             holder.html(result);
  24:             ajaxLoader.fadeOut(500);
  25:             $("div.chkGraphic").graphicCheckBox();
  26:         }
  27:     });
  28: }

Pretty standard stuff by now, but it can be made a whole lot better by using Wijmo. Let’s see how to do that.

Wijmo

Wijmo is a new product, complete kit of over 30 jQuery UI Widgets. It is a mixture of jQuery, CSS3, SVG, and HTML5. You can download it at www.wijmo.com.

The Wijmo grid has 20 different options that you can mix and match to make your grid look and behave how you would like. Detailed information about these options can be found at http://wijmo.com/wiki/index.php/Grid#ui.wijgrid_Options. For this demo, I am going to setup the grid with the following options: allowSorting, allowColumnSizing, allowPaging, and setting the page size to 10 rows.

As I mentioned before, when adding the references to your JavaScript files, the order of each reference is very important. After you download Wijmo, there will be several JavaScript files which will need to be included in your page.

   1: <script type="text/javascript" 
   2:   src="[Path]/Wijmo-Open/development-bundle/external/
   3:   jquery-1.4.3.min.js"></script>
   4:  
   5: <script type="text/javascript" 
   6:   src="[Path]/Wijmo-Open/development-bundle/external/
   7:   jquery-ui-1.8.6.custom.min.js"></script>
   8:  
   9: <script type="text/javascript" 
  10:   src="[Path]/Wijmo-Open/development-bundle/external/
  11:   jquery.bgiframe-2.1.3-pre.js"></script>
  12:  
  13: <script type="text/javascript" 
  14:   src="[Path]/Wijmo-Open/development-bundle/external/
  15:   jquery.glob.min.js"></script>
  16:  
  17: <script type="text/javascript" 
  18:   src="[Path]/Wijmo-Complete/development-bundle/external/
  19:   jquery.mousewheel.min.js"></script>
  20:  
  21: <script type="text/javascript" 
  22:   src="[Path]/Wijmo-Complete/development-bundle/external/
  23:   jquery.tmpl.min.js"></script>
  24:  
  25: <script type="text/javascript" 
  26:   src="[PathToJavaScript]/Wijmo-Complete/development-bundle/external/
  27:   raphael.js"></script>
  28:  
  29: <script type="text/javascript" 
  30:   src="[PathToJavaScript]/Wijmo-Open/js/
  31:   jquery.wijmo-open.1.0.0.min.js"></script>
  32:  
  33: <script type="text/javascript" 
  34:   src="[PathToJavaScript]/Wijmo-Complete/js/
  35:   jquery.wijmo-complete.1.0.0.min.js"></script>

You will also need to reference the Wijmo style sheets as well.

   1: <link href=”[Path]/Wijmo-Complete/development-bundle/themes/
   2:   ui-lightness/jquery-ui-1.8.4.custom.css" rel=”stylesheet
   3:   type=”text/css/>
   4:  
   5: <link href=”[Path]/Wijmo-Open/css/jquery.wijmo-open.1.0.0.css" 
   6:   rel=”stylesheettype=”text/css/>
   7:  
   8: <link href=”[Path]/Wijmo-Complete/css/jquery.wijmo-complete.1.0.0.css" 
   9:   rel=”stylesheettype=”text/css/>

Initializing the Wijmo grid is done the same way you do other things in jQuery, by getting an element in the page, in this case the table with an ID of “studentTable”, then adding the jQuery plugin to the element.

   1: $("#studentTable").parent().wijgrid({ 
   2:     allowSorting: true, 
   3:     allowColSizing: true, 
   4:     allowPaging: true, 
   5:     pageSize: 10 
   6: });

Here is the result.

0027.image_27A06894

As you can see the graphical check buttons I talked about in the last article are still there, and the grid has a nifty pager at the bottom.

The allowSorting option adds sorting capability on each column as seen here.

4578.image_52985699

While the “allowColSizing” lets you adjust the widths of each column to better view your data.

1300.image_1A9662B6

Another great option with the Wijmo grid is to allow editing to your data. In the jQuery to initialize the Wijmo grid, add another option, allowEditing:true.

   1: $("#studentTable").wijgrid({ 
   2:     allowSorting: true, 
   3:     allowColSizing: true, 
   4:     allowPaging: true, 
   5:     pageSize: 10, 
   6:     allowEditing: true 
   7: });

When the grid is rendered, each table cell will now become editable. All you have to do is click in the cell and start making any changes you need. The Wijmo Grid exposes events so you can handle the changed data however you prefer to.

There are 15 events total in the Wijmo Grid, and details on each of them are at http://wijmo.com/wiki/index.php/Grid#ui.wijgrid_Events. The Wijmo grid is very feature rich and I would highly recommend you play around with it.

Other Wijmo goodies

No more do you have to deal with boring old form elements on your page. Wijmo has decorators for Radio Buttons, Checkboxes, Drop Downs and Text fields. Adding these decorators is as easy as adding an additional jQuery call to your selected element.

So, by adding the following to my text fields which filter by student name.

   1: $("input#txtFilterFName").focus(function () { 
   2:     $("input#txtFilterLName").val(""); }).keyup(function () { 
   3:     getStudentsByName(this); }).wijtextboxdecorator();
   4:  
   5: $("#chkPaging").wijcheckboxdecorator();
   6:     $("#txtPageSize").wijtextboxdecorator();
   7:     $("input[type=radio]").wijradiobuttondecorator();

I get these spiffy looking text fields and form elements.

7484.image_52F463C1

One thing you may have noticed is that the grid and the form elements all have the same theme. This is because of the jQuery UI theming that is built into Wijmo. If you don’t like how the elements look you can create your own themes with the jQuery UI Theme Roller at http://jqueryui.com/themeroller/. When you get your theme just how you want it to, all it takes is a download, and referencing your theme’s particular style sheet in your page.

7026.image_43051EFD

   1: <link href="[Path]/Content/ui-lightness/jquery-ui-1.8.4.custom.css"
   2:     rel="stylesheet" type="text/css" />

In this article we talked more about jQuery plugins, the Wijmo gallery of widgets and how they can be used to create dynamic and very professional looking web applications. I hope you have enjoyed this series and have learned a few tricks. And as always, I would love to read your comments on my blog.

Happy Programming,

James

New Mexico .NET Users Group

By James at December 06, 2010 18:58
Filed Under: user groups, community

Last Thursday, December 12, I had the opportunity to go to Albuquerque, New Mexico and present at the New Mexico .NET Users Group. My flights were uneventful, checking into the hotel a breeze, and I got a free upgrade on the rental car, all that was left was to meet the group and give them my presentation, “ASP.NET MVC, jQuery, Ajax, EF and some other groovy stuff thrown in”. After the typical networking between members, the meeting started promptly at 7:00 pm, with some announcements by the president Tom Murdock. I was expecting a typical length meeting, but had been told just prior that their meetings usually go to 8:30 pm. Uh-oh I think, “two and half hours in 75 minutes”. Knowing that I would have less time I sneakily started my presentation a bit early, asking the usual questions I start off with – number of web and windows developers, C# vs. VB, who does their own database admin, etc.

My presentation turned into more of a lightning talk and actually it turned out pretty well. There was still time for audience questions and comments, I was focused on the code and concept so there wasn’t any time for tangents, and even had a little back and forth with one young kid who kept teasing me about not using PHP. I was able to tease him back and the whole presentation turned out very enjoyable. And as it turned out, I was able to go until 9:00 pm.

The attendees treat each other like family members, which was great to see. One of the things I noticed, and bumped up my ego several notches was to see many of the attendees taking notes on what I said. I added several more twitter followers, and three people actually commented on my presentation at www.speakerrate.com. Tom has grown a great group and should be congratulated for all his accomplishments. Best of all, they invited me back for another presentation.

All in all, one of the best visits I’ve had!

Here’s some pictures

4380.IMG_2511_277FED2C 0676.IMG_2512_248A8879
5621.IMG_2513_687E46C3 1207.IMG_2514_30E885D5
1374.IMG_2515_75487714  
Tom Murdock – right and ComponentOne Studio winner, James Archuleta  

Happy Programming

James

jQuery and MVC–Part 3

By James at November 19, 2010 19:11
Filed Under: JavaScript, MVC, jQuery, jQuery Plugins

In the last article I talked about MVC and doing some nifty Ajax functionalities with jQuery. In this article I will go over jQuery plugins, what they are and how to create them.

jQuery Plugins

jQuery has a plugin architecture which allows you to package up commonly used functions into a plugin. Chances are if you have a complex JavaScript function, someone has more than likely written a plugin to handle that particular functionality. The jQuery.com site has an extensive plugin library and it can sometimes be fun to browse through it to see what people have come up with.

Plugins are fairly easy to write, once you have gotten the basics of jQuery down. Here is a tooltip plugin that I wrote in just a few minutes.

   1: (function ($) {
   2:     $.fn.tooltip = function (options) { // make sure to name the function
   3:                                         // the same as your tooltip
   4:         var methods = {
   5:             
   6:             //initialize stuff
   7:             init: function (options) { },
   8:             
   9:             //destroy and clean up
  10:             destroy: function () {
  11:                 return this.each(function () {
  12:                     $(window).unbind("tooltip");
  13:                 })
  14:             }
  15:         };
  16:  
  17:         // default settings if they aren't passed in
  18:         var settings = {
  19:             'backgroundColor': 'red',
  20:             'color': 'blue',
  21:             'toolTipText': 'Hi',
  22:             'fontWeight': 'bold'
  23:         };
  24:  
  25:         // add the settings into the options
  26:         return this.each(function () {
  27:             if (options) {
  28:                 $.extend(settings, options);
  29:             }
  30:  
  31:             var $this = $(this);
  32:             $this.mouseenter(function (event) {
  33:                 $this.css("color", settings.colorEnter);
  34:                 
  35:                 // if the div#toolTip isn't in the DOM, create a new one
  36:                 if (!$("#toolTip").length) {
  37:                     // create the element
  38:                     $('<div id="toolTip"></div>')
  39:                         // set the text to passed option
  40:                         .html(settings.toolTipText)
  41:                         // from the style sheet
  42:                         .addClass("toolTip")
  43:                         // insert the div into the DOM just after $this
  44:                         .insertAfter($this)
  45:                         // position the div
  46:                         .css({ "left": $this.width(),
  47:                         // set the backgroundColor
  48:                         "backgroundColor": settings.backgroundColor,
  49:                         // set the font
  50:                         "fontWeight": settings.fontWeight,
  51:                         // set the color
  52:                         "color": settings.color
  53:                     }); 
  54:                 }
  55:                 $("#toolTip").fadeIn(500);
  56:             });
  57:  
  58:             $this.mouseleave(function () {
  59:                 $this.css("color", settings.colorLeave);
  60:                 $("#toolTip").fadeOut(500);
  61:             });
  62:         });
  63:     };
  64: })(jQuery);

It’s not a pretty thing, or even that functional, but it does show off how fast you can write a plugin. Let’s take a look at how this plugin is implemented.

1. In the <head> tag of your page include the references to your jQuery and JavaScript files.

   1: <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
   2: <script src="Scripts/jquery.tooltip.js" type="text/javascript"></script>
   3: <script src="Scripts/AppScripts.js" type="text/javascript"></script>

2. Somewhere on your page add an element that will call the plugin

   1: <div class="test">This is the element that will have the assignment</div>

3. In your custom JavaScript, attach the plugin to the element and pass in any options you need to.

   1: $(document).ready(function () {
   2:     $("div.test").tooltip({ "toolTipText": "Hello from ComponentOne", 
   3:         "color":"white" })
   4:         .click(function () { alert("hi"); });
   5: });

4. Add the following to your style sheet to style up the test div.

   1: .test{width:200px;
   2:    float:left;background:silver;
   3:    color:White;border:1px solid White;
   4:    cursor:pointer;padding:5px}

5. Open the page in a browser, then hover over the test div. If all goes well, you should see the following.

 

1447.image_12F3BB96

5287.image_2A3EC007

Obviously this isn’t the world’s greatest plugin, but does cover the basics of getting started. Information on building more functional plugins can be found at http://docs.jquery.com/Plugins/Authoring

TableSorter, a more functional plugin

Since we are working with tabular data in this application, I want to use a plugin that will modify an HTML table and make it sortable. Searching through the plugins library I found “TableSorter” at http://tablesorter.com. It is written by Christian Bach at www.lovepeacenukes.com and does a great job of providing a pretty nifty data grid from a static HTML Table.

Let’s take a look.

2744.image_3AD6BAF5

In the head tag of the HTML file place a reference to the TableSorter plugin right after the reference to the jQuery file.

2474.image_05114FCE

In the View in which you want to show the table, write your code to render a table with the data you want to show. One thing about the TableSorter plugin is that it requires valid HTML tables complete with <thead> and <tbody> tags. The following markup is written with the new MVC 3 Razor View Engine.

   1: <table id="studentTable" class="studentTable {sortlist: [[6,0]]}">
   2:     // sortlist is a function of the plugin which automatically sorts
   3:     // on a specific column
   4:     <thead>
   5:     <tr>            
   6:         <th>First Name</th>
   7:         <th>Last Name</th>
   8:         <th>Email</th>
   9:         <th class="{sorter: false}">Comments</th> 
  10:         // sorter class is a plugin option
  11:         <th>Submit Date</th>
  12:         <th>Approved Date</th>
  13:         <th>Approved</th>
  14:     </tr>
  15:     </thead>
  16:     <tbody>    
  17:     @foreach (var item in Model)
  18:     {        
  19:         var approvedDate = string.Empty;
  20:         if (item.ApprovedDate.HasValue) { 
  21:             approvedDate = item.ApprovedDate.Value.ToShortDateString(); 
  22:         }
  23:           <tr>               
  24:             <td>@item.FirstName</td>
  25:             <td>@item.LastName</td>
  26:             <td>@item.Email</td>
  27:             <td>@item.Comments</td>
  28:             <td>@item.SubmitDate.ToShortDateString()</td>
  29:             <td>@approvedDate</td>
  30:             <td>
  31:               <span style="display:none" class="hiddenSpan">@item.Approved</span>
  32:               @Html.Hidden("Approved", item.Approved)
  33:               @Html.Hidden("studentId", item.Id)
  34:               @{var chk = "chkGraphic chkUnChecked";}
  35:               @if (item.Approved.HasValue && item.Approved.Value.Equals(true))
  36:               {
  37:                   chk = "chkGraphic chkChecked";
  38:               }
  39:               <div class="@chk"></div>
  40:               <img src="../../Content/Images/ajax-loader.gif" 
  41:               class="ajaxLoader ajaxLoaderSmall" />
  42:             </td>
  43:         </tr>
  44:     }    
  45:     </tbody>
  46: </table>

Since we are rendering a table, we can use the $(document).ready(function(){});

Add the following in your custom JavaScript file:

   1: /// <reference path = "/Scripts/jquery-1.4.1-vsdoc.js"/>
   2:  
   3: $(document).ready(function () {
   4:     $("#studentTable").tablesorter({ widgets: ['zebra'] });
   5:     // zebra is a “widget” included in the plugin which will alternate
   6:     // the row colors of the table
   7: });

Run the page and sort the columns. One thing to note in the markup is in the last column. The plugin sorts on the data contained in the columns of each row. Since I want to sort on “Approved”, I add a hidden field with that data as the first element in the cell.

7024.image_5BBA07CF

2133.image_5713D748

The TableSorter plugin has many more options than this brief demo show. If you want to explore it more, download it at http://tablesorter.com.

A Freebie

If you look at lines 30 - 42 in the markup you may be wondering what all the other stuff is, including the <div class=”@chk”></div>. I wanted to spiff up the form and use graphics for the checkboxes, but still keep the functionality of a checkbox. Here is how to do it.

1. In the style sheet add the following classes.

   1: .chkGraphic{cursor:pointer;width:22px;height:22px;float:left;margin-right:5px}
   2: .chkChecked{background:url(Images/checkBoxOn.png)no-repeat}
   3: .chkUnChecked{background:url(Images/checkBoxOff.png)no-repeat}

2. In your custom JavaScript file, add the following functions.

   1: $("div.chkGraphic").live("click", function () { toggleGraphicCheckBox(this); });
   2:  
   3: function toggleGraphicCheckBox(elem) {
   4:     var $elem = $(elem);
   5:     $elem.siblings("img.ajaxLoader").show();
   6:     var valueField = $elem.siblings("input[type=hidden][name=Approved]");
   7:     if ($elem.hasClass("chkChecked")) {
   8:         $elem.removeClass("chkChecked").addClass("chkUnChecked");
   9:         valueField.val("False");
  10:     } else {
  11:         $elem.removeClass("chkUnChecked").addClass("chkChecked");
  12:         valueField.val("True");
  13:     }
  14:     $("span.hiddenSpan").html(valueField.val());
  15:     toggleStudentApproval($elem);
  16: }
  17:  
  18: function toggleStudentApproval(elem) {    
  19:     var approved = elem.siblings("input[type=hidden][name=Approved]").val();
  20:     var studentId = elem.siblings("input[type=hidden][name=studentId]").val();
  21:     $.ajax({
  22:         url: "/Home/GetStudent/" + studentId + "/" + approved,
  23:         type: "GET",
  24:         success: function (result) {
  25:             showStudentApproval(result, elem);
  26:         }
  27:     });
  28: }
  29:  
  30: function showStudentApproval(result, elem) {    
  31:     var id = result.Id;
  32:     var td = elem.parent("td").prev("td");
  33:     td.html("");
  34:     if (result.ApprovedDate != null)
  35:         td.html(result.ApprovedDate);
  36:     elem.siblings("img.ajaxLoader").fadeOut(500);
  37: }

Here’s the markup again.

   30: <td>
   32:  <span style="display:none" class="hiddenSpan">@item.Approved</span>
   33:  @Html.Hidden("Approved", item.Approved)
   34:  @Html.Hidden("studentId", item.Id)
   35:  @{var chk = "chkGraphic chkUnChecked";}
   36:  @if (item.Approved.HasValue && item.Approved.Value.Equals(true))
   37:  {
   38:     chk = "chkGraphic chkChecked";
   39:  }
  40:  <div class="@chk"></div>
  41:  <img src="../../Content/Images/ajax-loader.gif" 
  42:        class="ajaxLoader ajaxLoaderSmall" />
  43: </td>

Now based on what you’ve read so far in this and the previous articles, take a look at all the code and see if you can figure out what will happen when the graphical checkbox is clicked. Add a comment to this article and let me know what you think it will do.

Just for grins and giggles, I converted the JavaScript above to a plugin, jquery.graphicCheckBox.js

   1: (function ($) {
   2:     $.fn.graphicCheckBox = function (options) {
   3:     var methods = {
   4:         // initialize stuff
   5:         init: function (options) { },
   6:         //destroy and clean up
   7:         destroy: function () {
   8:             return this.each(function () {
   9:                 $(window).undbind("graphicCheckBox");
  10:             })
  11:         }
  12:     };
  13:     // default settings if they aren't passed in
  14:     var settings = {
  15:         'checked': 'chkChecked',
  16:         'unchecked': 'chkUnChecked',
  17:         'urlToAction': '/Home/GetStudent/'
  18:     };
  19:         // add the settings into the options
  20:     return this.each(function () {
  21:         if (options) {
  22:             $.extend(settings, options);
  23:         }
  24:  
  25:         var $this = $(this);
  26:         $this.click(function (event) {
  27:             $this.siblings("img.ajaxLoader").show();
  28:             var valueField = $this.siblings("input[type=hidden][name=Approved]");
  29:             if ($this.hasClass(settings.checked)) {
  30:                 $this.removeClass(settings.checked).addClass(settings.unchecked);
  31:                 valueField.val("False");
  32:             } else {
  33:                 $this.removeClass(settings.unchecked).addClass(settings.checked);
  34:                 valueField.val("True");
  35:             }
  36:             $("span.hiddenSpan").html(valueField.val());
  37:             toggleApproval($this);
  38:         });
  39:     });
  40:     function toggleApproval(elem) {
  41:         var approved = elem.siblings("input[type=hidden][name=Approved]").val();
  42:         var id = elem.siblings("input[type=hidden][name=studentId]").val();
  43:         $.ajax({
  44:             url: settings.urlToAction + id + "/" + approved,
  45:             type: "GET",
  46:             success: function (result) {
  47:                 showApproval(result, elem);
  48:             }
  49:         });
  50:     }
  51:     function showApproval (result, elem) {
  52:         var id = result.Id;
  53:         var hid = elem.siblings("input[type=hidden][name=studentId]").val();
  54:         var td = elem.parent("td").prev("td");
  55:         td.html("");
  56:         if (result.ApprovedDate != null)
  57:             td.html(result.ApprovedDate);
  58:         elem.siblings("img.ajaxLoader").fadeOut(500);
  59:     }
  60: }
  61: })(jQuery);

In this article we talked about extending jQuery with plugins, how to write a simple plugin, and explored a fully functional table sorter plugin. I even threw in a groovy freebie for you.

Happy Programming,

James

San Gabriel Valley Developers Group

By James at November 18, 2010 17:47
Filed Under: user groups, evangelism

Last night (November 18, 2010) I had the privilege of presenting at the San Gabriel Valley Developers Group run by Adnan Masood and Richard Trinh. I gave my usual presentation on MVC and EF, and decided to jazz it up with some jQuery and Ajax. There was a wide variety of expertise in the group, so everyone got something out of the presentation. About half were really interested in the Ajax, the other in the data layer and what EF can provide. There was even some time to dig deep into EF, pretending there was a rogue DBA who kept changing the database schema and how EF can keep up with it. Lot’s of fun.

As usual, I peppered the members with questions, and tossed out the famous ComponentOne carabineer key chains. Two people actually got my references to POCO and EF Contexts “remaining the same” to late ‘70’s bands. There was a great back and forth with the members and a ton of great questions. A win-win all around.

Adnan and Richard do a bang up job of running the group, and deserve big pats on the back for all they do. If you are ever in Monrovia, CA on the 3rd Wednesday of the month and need to get your geek on, this is the place to be.

Here’s some pictures.

8168.sgv1_2FB78B77 3326.sgv2_01BA12F2
3365.sgv3_3B2CFD1C 4276.sgv4_6D1477D9
3603.sgv5_2C62059D 1108.sgv6_3F730C7C
   
8311.c1-winner-Richard-Chin_273F7C54
ComponentOne Studio Winner, Richard Chin with Adnan Masood

Happy programming,

James

Desert Code Camp

By James at November 15, 2010 21:40
Filed Under:

Last weekend was the Desert Code Camp in Chandler, AZ. This is a great code camp and is put on by my good friend Joe Guadagno. I was honored to be able to present my ASP.NET MVC and Entity Framework presentation, and I had a great time doing so. Fortunately for me, Joe scheduled my session for the last block, which allowed me to ramp up and really be prepared for the group. There were about 20 attendees in the session, with a wide variety of skill sets and experience. One of my favorite comments from an attendee was when I asked the crowd for a definition of an Entity Context. He responded with “an ecosystem” which prompted me to toss one of our infamous Carabineer key chains to him.

The group was great with lots of good questions and a real back and forth experience. Afterwards I got several positive comments and feedback from a few of the attendees, which made me feel like the whole thing was a success. My slides are on Slideshare.net at http://www.slideshare.net/latringo/aspnet-mvc-and-entity-framework-4

The code camp itself was a dead on success and Joe should be congratulated heartedly. Held at Chandler-Gilbert Community College, the Student Commons was the main staging place for both attendees and speakers. Wi-Fi was flowing freely, and there were tons of snacks and drinks during the day to keep everyone charged up. Afterwards was an after party hosted by Joe featuring hors d’oeuvres to gnosh on and some really nice local craft beers. It was a great way to wind down from a fast paced, tech filled day by talking to people I had never met, about all types of things.

Joe, you did a bang up job pulling this off and getting all the sponsors in line! This is one of the best Code Camps around.

Here’s a few pictures

0407.IMAG0021_0470573D 3051.IMAG0023_284908CA
One of the nicest Code Camp banners I’ve ever seen The staging area in the Student Commons
1805.IMAG0022_4C21BA57 0045.IMAG0024_4C5E3D8C
Joe heading off to handle something Attendees taking a break between sessions

I can’t wait for the next one.

James

jQuery and MVC Part 2

By James at November 15, 2010 17:46
Filed Under: JavaScript, MVC, jQuery

In our last episode we went over the basics of JavaScript and the jQuery library. In this installment I will show you the basics of MVC and how it renders HTML differently than Web Forms pages, and finish up showing some basic Ajax with jQuery.

When ASP.NET first arrived it was a good thing. Web Forms allowed developers to build websites in a way that was very similar to how Windows Forms applications where built, with drag and drop of controls and a “code behind” architecture. Web Forms served their purpose for many, many applications in the enterprise, and many commercial applications, both large and small were successfully built using this technology.

When “Web 2.0” arrived, complete with Ajaxy interactions, Microsoft put forth their own brand of controls to handle this, and for the most part this worked, and continues to work well. However the Web Forms framework makes it difficult to build clean, lean web sites, as the technology depends on many different things in the rendered HTML to make it usable when posting back to the server. Two of the main items are ViewState and Control Rendering.

ViewState is sent to the browser as a way to capture what is on the page, and what has been changed when the request is sent back to the browser. For ViewState to work, controls on the page need to be rendered with specifically named ID’s. This makes it difficult to use JavaScript to find elements on the page by their ID. For example adding a button to a page that is using an ASP.NET Master Page will render the following HTML:

Source code:

   1: <asp:Panel ID="Panel1" runat="server">
   2:     <asp:Button ID="btnClick" runat="server" Text="Button" />
   3: </asp:Panel>

Rendered HTML:

   1: <div id="MainContent_Panel1">    
   2:    <input type="submit" name="ctl00$MainContent$btnClick" 
   3:       value="Button" id="MainContent_btnClick" />    
   4: </div>

ViewState:

   1: <div class="aspNetHidden">
   2: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 
   3:    value="/wEPDwUJNjE3ODYxMzIwZGQjyMGaAJ/BFAzxJv1b+/lEXPaj4I/hCsdqNKZUozZiyw==" />
   4: </div>

If you notice you will see both the name and the id of the button has been changed to show that it is a child of the asp:Panel which is a child of the ASP.NET MasterPage. The ViewState in this example is fairly lean, but in large pages, it can become very large, and this is data the browser needs to download.

While this is all fine and dandy, trying to access these elements can be difficult, especially when trying to use jQuery’s selector mechanisims. You may think, that if you keep the name the same when traversing the DOM, will keep it good. But if for some reason the ID of the parent element changes in your code, then the names will change. There are some tricks you can use, but they are not the basis of this tutorial.

Now, let’s look at the same page when rendered with MVC:

Source code:

   1: <asp:Panel ID="Panel1" runat="server">
   2:     <input type="submit" value="button" />
   3: </asp:Panel>

Rendered HTML:

   1: <div id="MainContent_Panel1">    
   2:    <input type="submit" value="button" />    
   3: </div>

Now keep in mind that ASP.NET MVC does not typically use ASP.NET controls and in this example I am using a standard HTML input tag for the submit button.

The ASP.NET MVC Framework – What is it?

Now before we get into a big discussion about what is better and what is not, and all the underpinnings of “who moved my cheese”, MVC is yet another way to build web sites using the .NET Framework. It is a web application framework which implements the model-view-controller pattern of development. It is based on ASP.NET and allows the development of a web application to be built using three roles; Models – the data coming into and out the application, Views – the displayed pages, and Controllers – which handle the traffic coming in for HttpRequest and HttpResponse.

Microsoft released a CTP version of MVC in December, 2007, MVC version 1 in March 2009 and MVC version 2 in March 2010. They are currently working on version 3.

The default View engine for MVC is the Web Forms view engine and this view engine uses regular .aspx pages to design the layout of the user interaction pieces of the web site or web application. Instead of PostBacks any interactions are routed to the Controllers using a Routing mechanism.

A typical ASP.NET MVC application will have the following directory structure as seen in this figure:

3036.MVC-Folder-Structure_741D04C1

As you can see there are folders for Controllers, Models, and Views. Look closely and you will see that the Controllers names are associated with the folders in the Views folder. AccountController maps to Views\Account while HomeController maps to Views\Home. Views that are shared throughout the application are saved in the Views\Shared folder.

Let’s take a look.

4621.Student-Course-Admin_3568075B

The first part of the application allows the user to look up a student by ID. As we can see from this screen shot the student with an ID of 1 is Jeanine Abbott. There are many steps involved in doing this using HTML, CSS and jQuery, so let’s dive in.

Index.aspx has the following code:

   1: <div class="row">
   2:     Find student by ID
   3:     <input type="text" id="studentId" value=""  class="studentId" />
   4:     <input type="button" id="btnAjax" value="Get Student" />
   5:     <img src="../../Content/Images/ajax-loader.gif" class="ajaxLoader" 
   6:          alt="ajaxloader" />
   7: </div>
   8: <div class="row">
   9:    <div class="errorMessage"></div>
  10:    <div class="studentData">
  11:     <table>
  12:         <thead>
  13:         <tr>
  14:             <th>First Name</th>
  15:             <th>Last Name</th>
  16:             <th>Email</th>
  17:             <th>Submit Date</th>
  18:             <th>Approved</th>
  19:             <th>Approved Date</th>
  20:         </tr>
  21:     </thead>
  22:     <tbody>
  23:         <tr>
  24:             <td class="studentFirstName"></td>
  25:             <td class="studentLastName"></td>
  26:             <td class="studentEmail"></td>
  27:             <td class="studentSubmitDate"></td>
  28:             <td class="studentApproved"></td>
  29:             <td class="studentApprovedDate"></td>
  30:         </tr>
  31:     </tbody>
  32:     </table>
  33:   </div>
  34: </div>

The next piece is in the HomeController where we have a method called GetStudent(int id)

   1: private readonly CourseEntities _db = ModelHelper.CourseEntities;
   2: public JsonResult GetStudent(int id)
   3: {
   4:     var student = (from s in _db.Students
   5:                    where s.Id.Equals(id)
   6:                    select s).FirstOrDefault();
   7:  
   8:     if (student == null) 
   9:         return Json("error:Student not found.", JsonRequestBehavior.AllowGet);
  10:  
  11:     var singleStudent = new SingleStudent
  12:         {
  13:             Id = student.Id,
  14:             FirstName = student.FirstName,
  15:             LastName = student.LastName,
  16:             Email = student.Email,
  17:             SubmitDate = student.SubmitDate.ToShortDateString(),
  18:             Approved = student.Approved.ToString(),
  19:             ApprovedDate = string.Empty
  20:         };
  21:  
  22:     if(student.ApprovedDate.HasValue)
  23:         singleStudent.ApprovedDate = student.ApprovedDate.Value.ToShortDateString();
  24:     
  25:     return Json(singleStudent, JsonRequestBehavior.AllowGet);
  26: }

The third piece of the puzzle is in the CSS. Looking at the ASPX code there are two div elements with classes of “studentData ” and “errorMessage”. In the CSS for this project these two classes have been defined as:

   1: div.studentData{display:none}
   2: div.errorMessage{display:none;color:Red;font-weight:bold;}

This makes these elements, and any elements contained within them to not be displayed when the page is first rendered.

The last piece to come into play is the JavaScript and jQuery. It is important to know that when adding your script references to your page, to make sure they are in the correct order. When you are going to use jQuery in your application and additional JavaScript files, the reference to jQuery needs to be first.

   1: <head id="Head1" runat="server">
   2:     <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
   3:     <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
   4:     <script src="../../Scripts/AppScripts.js" type="text/javascript"></script>
   5: </head>

I have written an additional JavaScript file, “AppScripts.js” and included it in the Scripts folder of the application. And by adding a reference to “jquery-1.4.1-vsdoc.js” to your additional JavaScript files, Visual Studio will provide jQuery Intellisense to make it easier reference the jQuery functions and attributes.

   1: /// <reference path = "/Scripts/jquery-1.4.1-vsdoc.js"/>
   2:  
   3: $(document).ready(function () {
   4:     $("#btnAjax").click(function () { getStudent(); });    
   5: });

Going back to the first article in this series, you can see that when the document has been fully loaded into the DOM, with $(document).ready(), the element with the ID of “btnAjax” will have a click event assigned to it. The JavaScript function getStudent() is below.

   1: function getStudent(id) {
   2:     $("img.ajaxLoader").fadeIn(500);
   3:     $("div.errorMessage").fadeOut(500);
   4:     $("div.studentData").fadeOut(500);
   5:     $.ajax({
   6:         url: "/Home/GetStudent/" + $("#studentId").val(),        
   7:         type: "GET",
   8:         success: function (result) {
   9:             if (typeof (result) == "string" && result.indexOf("error:") > -1) {
  10:                 showError(result);
  11:             } else {
  12:                 showStudentData(result);
  13:             }
  14:         }
  15:     });
  16: }

Let’s step through this. Line 1 hides the ajaxLoader image, lines 2 and 3 hide the errorMessage and studentData div tags. Line 4 starts the jQuery ajax function by defining the url, the form action type, and what to do on a successful response. Because I am using a “GET” in the ajax code, I don’t have to include a <form> on the page. Looking back to the GetStudent(int id) method in the Controller, it is expecting a parameter of the type int to be passed in as well. This is done by getting the value of the text field which has an ID of “studentId”;

How the routing is setup with the application, the MVC framework will automatically cast the “1” to an int before it is passed into the method.

The GetStudent () method will take the ID passed in then select the student with a matching ID. If no student is found, GetStudent() will return a string, “error:Student not found.” If a student is found, then the method will return a Json object containing the data of the student.

Using Firebug for Firefox is a good way to see the data which is coming back from the Controller.

When an error is returned, in this case, the student was not found

3036.GetStudent-with-Error_33D6C57A

6557.GetStudent-with-Error-web-page_44028D73

When a student object is returned

5086.GetStudent-with-student_1B83AB5F

5873.GetStudent-with-student-web-page_604FCF93

The success attribute of the $.ajax function will check the type of the result coming back. If it is a string, the showError() function is called. If the type of the result coming back is an object, we know it is a JSON object and the showStudentData() function is called which will display the student’s data.

   1: function showError(result) {
   2:     var msg = result.split(":");
   3:     $("div.errorMessage").html(msg[1]).fadeIn(500);
   4:     $("img.ajaxLoader").fadeOut(500);
   5: }
   6:  
   7: function showStudentData(student) {
   8:     $("td.studentFirstName").html(student.FirstName);
   9:     $("td.studentLastName").html(student.LastName);
  10:     $("td.studentEmail").html(student.Email);
  11:     $("td.studentSubmitDate").html(student.SubmitDate);
  12:     $("td.studentApproved").html(student.Approved.toLowerCase());
  13:     $("td.studentApprovedDate").html(student.ApprovedDate);
  14:     $("div.studentData").fadeIn(500);
  15:     $("img.ajaxLoader").fadeOut(500);
  16: }

Another example

In this project, I want to quickly approve a student for the current course. I could build an Edit Student form and approve them with that, but I don’t want to handle all of that overhead. All I want to do is click the Approved checkbox and go onto the next student.

I can do this by adding a click event to the checkboxes that have been rendered on the page with the following line of jQuery code.

   1: $("input[type=checkbox][name=Approved]").live("click", function () {
   2:     toggleStudentApproval($(this)); 
   3: });

Notice how I am using the “live” event to bind the click event to the checkboxes. What is cool about doing it this way is, the event will be bound to all selected elements now and in the future. So if the page is heavy with elements and long in loading, the $(document).ready() function may fire before all the elements have been rendered. You may also create new DOM elements with jQuery which won’t be available during $(document).ready(), so setting events with the .live() event is a good practice to start.

The JavaScript code:

   1: function toggleStudentApproval(elem) {
   2:     $(elem).siblings("img.ajaxLoader").fadeIn(500);
   3:     var approved = $(elem).attr("checked");
   4:     var studentId = $(elem).prev("input[type=hidden]").val();
   5:     $.ajax({
   6:         url: "/Home/SetStudentApproval/" + approved + "/" + studentId,
   7:         type: "GET",
   8:         success: function (result) {
   9:             showStudentApproval(result);            
  10:         }
  11:     });
  12: }

When the checkbox is clicked the code first shows the animated GIF, “ajaxLoader” that is a sibling of the checkbox. It then sets the variable “approved” to the “checked” attribute of the checkbox, then gets the value of the hidden field “studentId”. Line 6 shows the url which will be sent to the Controller, and line 9 shows the function to be called when the response is received. The request is then sent to the SetStudentApproval method in the Home Controller.

The Controller method, SetStudentApproval

   1: public JsonResult SetStudentApproval(bool approved, int id)
   2: {
   3:     var student = (from s in _db.Students
   4:                     where s.Id.Equals(id)
   5:                     select s).FirstOrDefault();
   6:  
   7:     if (student == null)
   8:         return Json("error:Student not found.", JsonRequestBehavior.AllowGet);
   9:  
  10:     student.Approved = approved;
  11:     student.ApprovedDate = DateTime.Now;
  12:     if (!approved)
  13:         student.ApprovedDate = null;
  14:     _db.SaveChanges();
  15:  
  16:     var singleStudent = new SingleStudent
  17:     {
  18:         Id = student.Id,
  19:         FirstName = student.FirstName,
  20:         LastName = student.LastName,
  21:         Email = student.Email,
  22:         SubmitDate = student.SubmitDate.ToShortDateString(),
  23:         Approved = student.Approved.ToString()
  24:     };
  25:  
  26:     if (student.ApprovedDate.HasValue)
  27:         singleStudent.ApprovedDate = student.ApprovedDate.Value.ToShortDateString();
  28:     
  29:     return Json(singleStudent, JsonRequestBehavior.AllowGet);
  30: }

This method is the similar to the GetStudent method earlier in the article. For ease in reading, I copied it and added passing in the “approved” status, then passing back the student object to the JavaScript function.

The additional JavaScript functions

   1: function showStudentApproval(result) {
   2:     var id = result.Id;
   3:     var hid = getHiddenField(id);
   4:     var td = hid.parent("td").prev("td");
   5:     td.html("");
   6:     if (result.ApprovedDate != null)
   7:         td.html(result.ApprovedDate);
   8:     hid.siblings("img.ajaxLoader").fadeOut(500);
   9: }
  10:  
  11: function getHiddenField(id) {
  12:     var hid;
  13:     $("input[type=hidden]").each(function () {
  14:         if ($(this).val() == id) {
  15:             hid = $(this);
  16:             return false;
  17:         }
  18:     });
  19:     return hid;
  20: }

The function showStudentApproval gets the Id of the student, then calls a helper function, getHiddenField to find the matching hidden field. This is so the table cell displaying the Approved Date can be found. The helper function gets an array of all hidden fields in the DOM, and uses the jQuery function .each() to loop through each one checking the value to see if it matches what was passed in. When it finds the correct element, the variable “hid” is set to the hidden field, then the looping is stopped. The hidden field is then sent back to the showStudentApproval function.

For ease, and since only the checkbox is visible, the hidden field, the checkbox and the animated GIF are all in the same table cell. Line 4 finds the table cell which is to the left of the cell holding these elements by traversing the DOM, looking first for the parent(), then parents previous sibling.

8688_nested-table-cell_0500E70B

Line 5 shows that once you have a jQuery object in memory, you don’t have to use the $() syntax to refer to it again. This is a best practice, as using the $() to find elements which you already have in memory will lead to a degradation in performance.

Line 5 clears out the html of the table cell we found by traversing the DOM in line 4. Lines 6 and 7 set the html of the table cell to the student’s ApprovedDate.

Step 1

2287.student-approval-1_63350E79

Step 2

4405.student-approval-2_61F0759A

Step 3

3718.student-approval-3_4BBA5A48

In this article we talked about some of the differences between a Web Forms application and a MVC application. Showed how to include jQuery files, and did some cool things with Ajax and jQuery to streamline an application’s functionality. In the next article, I will show you how to extend jQuery’s functionality with plugins.

Happy Programming,

James

About the author

James James is a five time and current Microsoft MVP in Client App Development, a Telerik Insider, a past Director on the INETA North America Board, a husband and dad, and has been developing software since the early days of Laser Discs and HyperCard stacks. As the Founder and President of the Inland Empire .NET User's Group, he has fondly watched it grow from a twice-a-month, early Saturday morning group of five in 2003, to a robust and rambunctious gathering of all types and sizes of .NET developers.

James loves to dig deep into the latest cutting edge technologies - sometimes with spectacular disasters - and spread the word about the latest and greatest bits, getting people excited about developing web sites and applications on the .NET platform, and using the best tools for the job. He tries to blog as often as he can, but usually gets distracted by EF, LINQ, MVC, ASP, SQL, XML, and most other types of acronyms. To keep calm James plays a mean Djembe and tries to practice his violin. You can follow him on twitter at @latringo.

And as usual, the comments, suggestions, writings and rants are my own, and really shouldn't reflect the opinions of my employer. That is, unless it really does.

James Twitter Feed

Recent Comments

Comment RSS

Month List