JQuery MVC Form Helper

Simple Form Helper

As I continue to use JQuery and MVC I am completely impressed with how much you can do with very little. I know the new preview 4 came out with an AjaxForm helper. I could not resist, so I made my own and added it to the whole controls project I've been working on. (I have not forgot about the grid, but I've gotten a little bored of it for the time being although I do want to finish it...). So here is the idea:

  1. Have complete control over HOW the form is rendered
  2. Be VERY light (I attached the whole thing to the onSubmit attribute on the form)
  3. Make it fast (it took me like a half an hour or so)

Using it

I found this nifty way of using an action delegate that "reads between the lines." The motivation came from the MVCContrib grids ability to completely define inline ASP.NET looking code and pass it into the helper. Here is how you use it (for some reason I find it useful to see how it works first, and then explain how it works):

   1:  <% Html.JQueryForm<Student, HomeController>("studentForm",
   2:         c => c.EditStudent(),
   3:         (ViewData.Model as IEnumerable<Student>).First<Student>(), 
   4:         s => 
   5:         { %>
   6:             <div><%= Html.TextBox("Id", s.Id.ToString()) %></div>
   7:             <div><%= Html.TextBox("FirstName", s.FirstName) %></div>
   8:             <div><%= Html.TextBox("LastName", s.LastName) %></div>
   9:             <div><%= Html.SubmitButton("Submit", "Submit") %></div>
  10:      <% }
  11:     ); 
  12:  %>

Parameter explanation:

  1. The name to give the form
  2. The action that should be invoked on the controller
  3. The POCO object that holds the data
  4. What to render (the Action delegate that says take a student S and make Html textboxes)

The types passed in are the POCO data object and the controller that will get the action.

Building it

I really just copied a lot of what I have already done with the grid (see previous posts) and made it more lightweight. First the helper:

   1:  public static void JQueryForm<T, TController>(this HtmlHelper helper, 
   2:      string name, Expression<Action<TController>> editAction, 
   3:      T data, 
   4:      Action<T> block) 
   5:      where T : class
   6:      where TController : Controller
   7:  {
   8:      Form f = new Form(name, 
   9:          helper.BuildUrlFromExpression<TController>(editAction), 
  10:          helper.ViewContext.HttpContext);
  11:      f.RenderOpen();
  12:      block.Invoke(data);
  13:      f.RenderClose();
  14:  }

Notice how simple it really is! Using the helper, I build a url from the controller expression, and then pass everything else into a Form object that only does 2 things: render the opening form tag, and render the closing form tag. The only catch is rendering the correct JavaScript code in the onSubmit attribute of the form tag. The rest was simple. Notice on line 4 above the delegate that takes the markup. This markup is rendered on line 11. For the "hard" part, I just copied the JQuery Ajax call from the Grid Control I have been working on. Here is the gist of it:

   1:  var formData = $(this).serializeArray();
   2:  $.ajax({
   3:        type : 'POST',
   4:        contentType : 'application/x-www-form-urlencoded',
   5:        url : 'edit ACTION here',
   6:        data : formData,
   7:        dataType : 'json',
   8:        success : function(msg){
   9:            alert('Complete! ('+msg+')')}
  10:       }
  11:   );
  12:  return false;

The return false is there to prevent the form from posting back.

Screenshot

Here are some screenshots of the whole thing:

The form rendered:

image

The postback:

image

Notice that I used the Request helper I built in a previous post. In order to do this, I am required to name the textboxes with the same name as the property of the object. This is how the helper resolves that appropriate attribute.

Code

Here is the code. You will find fragments of me starting to do the grid code as well.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Ajax HTML Grid Control for ASP.NET MVC (Part 3)

Moving to an HTML Helper

As I was looking around at the various HTML helpers out there, I realized that I should probably conform and stick to what is being done. Having said that, I refactored the Grid class to be a new HTML helper. Here is how it is used (similar to an MvcForm<T> actually):

<% using (Html.GridControl<StudentController, StudentEntity, int>(
   s => s.EditStudent(), 
   s => s.DeleteStudent(), 
   ViewData.Model, 
   s => s.StudentId, 
   "Student",
   null)) {  %>
Special text here!
<% } %>

The GridControl has now been attached as an extension method to the standard HTML Helper. Doing that required a bit of re-arranging on the parameters that needed to be passed in to the helper. The first are the types being used:

  1. The first type is the Controller being used for updates
  2. The second is the type of item in the collection
  3. The last is the data type of the key of each item

For the parameters:

  1. The first specifies the controller action when an item is edited
  2. The second specifies the controller action when a item is deleted
  3. The third is the actual Collection object
  4. The fourth tells the GridControl how to generate the key for each item
  5. The fifth is the name to use for the control
  6. The last specifies any additional html attributes to use (not implemented currently)

I really liked using the action expressions since it allows a better granularity in handling the actual Ajax requests to the controller. I used the this code to generate the appropriate URLS (strings):

_editAction = helper.BuildUrlFromExpression<T>(editAction);
_deleteAction = helper.BuildUrlFromExpression<T>(deleteAction);

An important thing to note is that the grid is actually printed out when the helper is disposed. This means that anything that is placed inside of the using brackets will be shown before the grid is displayed.

grid

Getting Data to the Controller

Now for the actual work! The first thing I wanted to ensure was that I sent the data back to the controller via a POST and not a GET method. The $.getJSON function in JQuery performs a GET (see post) so I needed to change that. Here is what the grid produced for the save and delete actions:

else if(action == 'save')
{
    var arr = $('#Student_Form').serializeArray();
    $.ajax({
       type: 'POST',
       contentType: 'application/x-www-form-urlencoded',
       url: '/Student/EditStudent',
       data: arr,
       dataType: 'json',
       success: function(msg){
         alert( 'Data Saved: ' + msg );
       }
     });
}
else if(action == 'delete')
{
    var dat = 'DataId=' + id;
    $.ajax({
       type: 'POST',
       contentType: 'application/x-www-form-urlencoded',
       url: '/Student/DeleteStudent',
       data: dat,
       dataType: 'json',
       success: function(msg){
         alert( 'Delete: ' + msg );
       }
     });
}

Notice that this is a continuation of the JavaScript from the previous post. The fundamental JQuery call for AJAX is $.ajax(properties) where the properties are the specifications on how the AJAX call should be made. The first and most important thing was to use POST rather than GET. A GET http call is supposed to be idempotent while a POST call can actually change the state of the server. Both the delete and save actions should change the state of the server. Also, from a security standpoint, it is always easier to craft some form of URL to cause an action to execute on a controller when using GET. This can open things up to malicious attacks. Imagine anyone simply typing http://yoursite/Student/DeleteStudent?DataId=1232 into their browser and the controller deleting that record. That would be bad. With a POST this can be controlled better.

My favorite part of the JQuery code is the .serializeArray() part. This takes each of the valid form members and automagically places the data in a JSON array structure to be passed to the controller.

Getting the Data in the Controller

Here is a nice screenshot of what happens when the controller save action is fired:

postback

The AJAX postback, in conjunction with the .serializeArray() call placed all of the items of the StudentEntity in the Request.Form collection. Now for the ugly code (reminiscent of ASP 3.0)

    public JsonResult EditStudent()
    {
        StudentEntity student = new StudentEntity();
        student.StudentId = int.Parse(Request.Form["StudentId"]);
        student.Address = Request.Form["Address"];
        
        int age;
        if (int.TryParse(Request.Form["Age"], out age)) student.Age = age;
        student.FirstName = Request.Form["FirstName"];
        student.LastName = Request.Form["LastName"];
        student.Phone = Request.Form["Phone"];
        student.State = Request.Form["State"];
        student.Zip = Request.Form["Zip"];
        
        StudentService.SaveStudent(student);
        
        return new JsonResult
        {
           Data = "Edited student " + student.StudentId.ToString() + "!"
        };
    }

The result sent back is a simple message. More could be done here, but I have not thought about it too much. I also dislike the whole Request.Form["val"] stuff. I think this could be automated using the grid somehow so we can get rid of the tedious code. Actually I think adding a generic static method to the Grid class should do the trick. I will probably add it next time.

Things to do

So far things seem to be going well. Here are some things I would like to add:

  1. Better entity population from the Request.Form collection
  2. An "Add New" feature to the grid
  3. On delete of a row, remove the TR element from the table (the database thinks it is gone, but the HTML says otherwise)
  4. CSS Grid customization

If you can think of any other things drop me a line.

Code

The Code

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Ajax HTML Grid Control for ASP.NET MVC (Part 2)

Preamble

Over the long weekend I thought a lot about where I wanted to go with the grid "control" for ASP.NET MVC. One of the things that weighed heavily on my mind was the ability to have the control fully customizable. As I thought about this, I decided that first thing's first: I need to get the functionality working and then worry about the prettiness factor. So for those of you concerned about the customizability - it's coming. For now, I really want to focus on the ability of the grid to get work done.

Code Bloat

As I began to dive again into the previous code I realized that having everything in one extension method was going to be... well ugly. The first order of business was to abstract everything in to a separate DLL. In order to do so, I created a new Grid class that handles all of the grid drawing.

JavaScript

I am partial to using JQuery. So here is the general idea of what the JavaScript code needs to do:

  1. Detect that the user would like to edit a particular row
  2. Detect any rows previously being edited (to move the edit focus to the new row being edited)
  3. Display text boxes for the user to have the ability to edit the values
  4. Post back an edit or a delete

In order to do each of the items above, I needed to change what the grid was outputting a little. First I needed to wrap the table in a <form> tag in order to retrieve any valid values being edited. Also, I needed to add the following links: Edit, Delete, Cancel, Save. The first two (Edit, Delete) should be shown when a row was not in edit mode. The second two (Cancel, Save) should only be shown when a row is in edit mode. I also wanted to make sure that only one row was being edited at a time. Here is a fragment of the HTML the control produces:

<form name="Student_Form" id="Student_Form">
<table id="Student" style="border: solid 1px black;width:100%;">
<tr id="Student_6">
    <td class="edit">
    <span class="editor">
       <a id="6_Student_edit" href="#">Edit</a>&nbsp;
       <a id="6_Student_delete" href="#">Delete</a>
    </span>
    <span class="editing">
       <a id="6_Student_cancel" href="#">Cancel</a>&nbsp;
       <a id="6_Student_save" href="#">Save</a>
    </span></td><td class="StudentId">6</td>
    <td class="FirstName">Frances</td>
    <td class="LastName">Adams</td>
    <td class="Age">&nbsp;</td>
    <td class="Address">&nbsp;</td>
    <td class="City">&nbsp;</td>
    <td class="State">&nbsp;</td>
    <td class="Zip">&nbsp;</td>
    <td class="Phone">&nbsp;</td>
</tr>
...

Once I decided on the DOM elements the form would produce, it was time to put some JavaScript to each row. The first bit of code is designed to attach events to each of the links in the grid as well as hide the "editing" spans since each row by default starts in non-edit mode:

$('document').ready(
    function(){
        $('#Student span.editing').hide();
        $('#Student a').click(
            function(event){
                event.preventDefault();
                handleEditClick(this.id);
            }
        );
    }
);

This code ensures that the editing spans are hidden and each click event on the anchor tags are redirected to the handleEditClick function. Notice that I pass in the id of the actual anchor tag. These I defined as id_Grid_action (see above). Whenever a link is pushed I get those three pieces of information to proceed with processing. Now for the handleClick function (it is long):

function handleEditClick(itm) {
    var o = itm.split('_');
    var id = o[0];
    var grid = o[1];
    var action = o[2];
    var name = '#' + grid + '_' + id + ' td';

    if(action == 'edit')
    {
        // un-edit any others that might be in editmode
        $('#Student span.editor:hidden a:first').each(
            function() {
                var cl = this.id.split('_');
                $('#' + cl[0] + '_' + cl[1] + '_cancel').click();
            }
        );

        $('.editor', name).hide();
        $('.editing', name).show();

        $(name).each(
            function() {
                if($(this).hasClass('edit')) return;
                var data = $(this).text() == '&nbsp;' ? '' : $(this).text();
                $(this).html('<input type="text" name="' + $(this).attr('class') + 
'" value="' + data + '" size="10" />'); } ); } else if(action == 'cancel') { $('.editor', name).show(); $('.editing', name).hide(); $(name).each( function() { if($(this).hasClass('edit')) return; var data = $('input', this).val(); $(this).html(data == '' ? 'nbsp;' : data); } ); } else if(action == 'save') { //alert('Save ' + itm); var arr = $('#Student_Form').serializeArray(); $.each(arr, function(i, field) { alert(field.name + ': ' + field.value); } ); } else if(action == 'delete') alert('Delete ' + itm);

As advertised, I first break up the id_gird_action pair into variables that will be useful. Also notice that ALL actions come into this function. First lets focus on the edit action. Now this is why I love JQuery:

$('#Student span.editor:hidden a:first').each(...

This particular line of code selects the first anchor tag under a span with class editor that is hidden from the Student grid. Why would I want to do that? From the id of the anchor tag I can reconstruct the id of the cancel anchor tag and then "click" it in order to cancel the update. To do it any other way would be difficult (at least I think so). The elegance of JQuery allows for those kinds of things. Once we cancel any other edit, we proceed to go through each TD in the row in question (with the exception of the edit anchors) and push the data into text boxes. Also, we set the appropriate edit spans to visible and hidden in order to have the correct actions displayed. Next, the Cancel action. The job of the cancel action is to take the data out of the text boxes and stuff them back into the TD tag. I realize that I should probably add a "Do you want to save this?" confirm box, but I will leave that for later. Notice again the elegance of JQuery:

var data = $('input', this).val();

The $(INPUT, TD) functions as a selection within a previous selection. In other words, within the current TD, find me an INPUT HTML element and retrieve the value. Once we retrieve the value, we can put it back into the TD tag without the INPUT element.

The Grid Class

The first thing I did was write the JavaScript code with the output from the Grid class. In other words, I ran the flat table (without the JavaScript) and cut and pasted the table to a standard HTML file. Once I had the new HTML file I worked on the JavaScript until it worked as I expected. Now the only problem left was creating  a RenderJavaScript() that emitted the Grid specific JavaScript we needed. Doing that seemed a bit tedious so I downloaded a nifty little Add In that did it for me.

Outcome

Some screens:

image

Usage

Now that I've abstracted the Grid out to a completely separate project, I changed the helper class to this:

public static string ToAjaxGrid<T, TKey>(this IEnumerable<T> list, 
                                 Func<T, TKey> key, 
                                 string name)
{
   Grid<T, TKey> grid = new Grid<T,TKey>(list, key, name);
   return grid.Render();
}

This change allowed me to leave the code in the View the same:

<%= ViewData.Model.ToAjaxGrid<AjaxHelpers.Models.StudentEntity, int>(s => s.StudentId, "Student") %>

Next Time

I think the client side functionality is (mostly) done. For the next installment I will try to actually submit the requested actions to the MVC controller (save, delete).

Code

Download

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Using JQuery to perform Ajax calls in ASP.NET MVC

As I was looking around for ways to enable Ajax in the current ASP.NET MVC release I found a couple of things that I found pretty useful and thought I should share.

The J in Ajax (JQuery)

The muscle behind the actual asynchronous calls comes from JavaScript. I looked around at a bunch of existing JavaScript libraries and settled on JQuery because of the way it leverages existing CSS knowledge. The three things that the library should do easily are:

  1. Help me easily display an "updater" to let user know something is happening (i.e. loading data),
  2. Assist in making the actual Ajax call without any hassle,
  3. and, most importantly, let me get 1 and 2 working without headaches.

Here is how JQuery helps in the three cases:

  1. The "updater":
    $('#updater').show();
    $('#updater').hide();
    Notice the way JQuery uses the standard CSS id selector. Could it be any easier?
  2. JQuery has the following Ajax calls available in its library:
    object.load( )
    $.get( )
    $.post( )
    $.getJSON( )
    This takes away (hides) all of the XmlHttp object nonsense from the whole Ajax call.
  3. See 1 and 2

ASP.NET MVC

There are tons of good posts/tutorials on exactly how the ASP.NET MVC model works so I will not attempt to get into it too much here. The most important thing to know is that there are three things working together:

  1. The Controller,
  2. The Model, and
  3. The View

The controller handles all of the requests, asks the model for data, and then instructs the view to present the data (if any) returned.

Routes

One of the neat things about the MVC framework is the notion of routes. The default route setup is as follows:

   1:  routes.MapRoute(
   2:          "Default",                                              // Route name
   3:          "{controller}/{action}/{id}",                           // URL with parameters
   4:          new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
   5:  );

This simply means that, from the root of your web app, whenever a URL of the form http://root/foo/baz/bar is presented, the routing engine will call the foo controller with the baz action while supplying it with the bar id.

Some Code

Enough explanation, now to some code!

The Model

Since this is not an exercise in using the database, I created a simple Model class for students:

   1:  public class Student
   2:  {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:     public int StudentId { get; set; }
   6:          
   7:     public static IQueryable<Student> GetStudentDataList()
   8:     {
   9:        return new List<Student>() 
  10:        {
  11:           new Student { FirstName = "John", LastName = "Smith", StudentId = 1},
  12:           new Student { FirstName = "Susan", LastName = "Connor", StudentId = 2},
  13:           new Student { FirstName = "Bryan", LastName = "Jones", StudentId = 3},
  14:           new Student { FirstName = "Lucy", LastName = "Vargas", StudentId = 4},
  15:           new Student { FirstName = "Robert", LastName = "Jimenez", StudentId = 5},
  16:           new Student { FirstName = "Seth", LastName = "Juarez", StudentId = 6},
  17:           new Student { FirstName = "David", LastName = "Meeks", StudentId = 7},
  18:           new Student { FirstName = "Olivia", LastName = "Rassmusen", StudentId = 8},
  19:           new Student { FirstName = "Viola", LastName = "Masterson", StudentId = 9},
  20:           new Student { FirstName = "Russel", LastName = "Jones", StudentId = 10}
  21:        }
  22:        .AsQueryable<Student>();
  23:     }
  24:  }

This simply creates a new list of Students and returns them as an IQueryable.

The Controller

Since I want to only pass JSON serialized objects over the wire when making the Ajax calls, there is a handy return type for the action in the controller called JsonResult:

   1:  public JsonResult Find(string name)
   2:  {
   3:     // To simulate "wait"
   4:     System.Threading.Thread.Sleep(1500);
   5:          
   6:     return new JsonResult
   7:     {
   8:        Data = (from student in Student.GetStudentDataList()
   9:                where student.LastName.StartsWith(name)
  10:                select student).ToArray<Student>()
  11:     };
  12:  }

This will serialize the data out in the JSON format. Now for the actual interesting part:

The View

The html for this example is VERY simple:

   1:  <div id="query">
   2:        <%= Html.TextBox("textSearch") %>&nbsp;
   3:        <a href="#" id="linkFind">Find</a>&nbsp;&nbsp;&nbsp; 
   4:        <span class="update" id="updater">
   5:              <img src="<%= AppHelper.ImageUrl("indicator.gif") %>" alt="Loading" />
   6:              &nbsp;Loading...&nbsp;&nbsp;&nbsp;
   7:        </span>
   8:  </div>
   9:   <div class="label">Students:</div> 
  10:  <div id="studentList"></div>

There is a search box, a link, and update panel, and a div (studentList) that will be populated by the Ajax call. This is where the magic of the routing engine coupled with the JQuery Ajax call comes together:

 

   1:  $(document).ready(
   2:     function()
   3:     {
   4:        // Hide update box
   5:        $('#updater').hide();
   6:                  
   7:        var retrieveData = function(path,  query,  funStart,  funEnd, funHandleData)
   8:        {
   9:           // for displaying updater
  10:           funStart();
  11:                          
  12:           // retrieve JSON result
  13:           $.getJSON(
  14:              path,
  15:              { name : query },
  16:              function(data)
  17:              {
  18:                 // handle incoming data
  19:                 funHandleData(data);
  20:                 // for hiding updater
  21:                 funEnd();
  22:              }
  23:           );
  24:        };
  25:                  
  26:        // adding handling to find link
  27:        $('#linkFind').click(
  28:           function(event)
  29:           {
  30:              event.preventDefault();
  31:              retrieveData(
  32:                 // mvc route to JSON request
  33:                 '/Student/Find/', 
  34:                 // query from textbox
  35:                 $('#textSearch')[0].value, 
  36:                 // function to show updater
  37:                 function() { $('#updater').show(); }, 
  38:                 // function to hide updater
  39:                 function() { $('#updater').hide(); },
  40:                 // retrieving the data
  41:                 function(data)
  42:                 {
  43:                    // clear students (if any)
  44:                    $('#studentList > div').remove();
  45:                    // add students
  46:                    for(s in data)
  47:                    {
  48:                       var student = data[s];
  49:                       $('#studentList').append('<div>(' +  student.StudentId ... you get the idea                 
  50:                     }
  51:                 }
  52:              );
  53:            }
  54:         );
  55:      }
  56:  );

The retrieveData function uses the JQuery $.getJSON call to retrieve the data. It takes as arguments the path to the controller (remembering routes), the argument (in this case the query) that the controller action expects, as well as a couple of functions. These functions will handle the cases where the  request is started, the request is ended, and what to do with the data that is returned. Subsequently we add the event handler to the link that initiates the find request. Notice that we are passing in '/Student/Find/' as the path since we are using the Find action on the StudentController. The argument is passed in from the search text box. This sets into motion the asynchronous request to the controller. The controller then returns a JSON serialized array of Student objects. Once the data is received, the studentList div is then cleared and repopulated.

Screenshot

The request:

request

The response:

response

Conclusion

Hopefully this has been helpful! It was fun putting everything together. Feel free to send me an email or leave a comment on this post if there are any suggestions/comments.

Code

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: