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:
- Help me easily display an "updater" to let user know something is happening (i.e. loading data),
- Assist in making the actual Ajax call without any hassle,
- and, most importantly, let me get 1 and 2 working without headaches.
Here is how JQuery helps in the three cases:
- The "updater":
$('#updater').show();
$('#updater').hide();
Notice the way jQuery uses the standard CSS id selector. Could it be any easier? - 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. - 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:
- The Controller,
- The Model, and
- 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") %>
3: <a href="#" id="linkFind">Find</a>
4: <span class="update" id="updater">
5: <img src="<%= AppHelper.ImageUrl("indicator.gif") %>" alt="Loading" />
6: Loading...
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:
The 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.