Sunday, 13 December 2009

To View Model or not to View Model?

Up to now in developing web applications using ASP.Net MVC I've eschewed the use of View Models and have simply passed my Domain Model objects into views, along with supplementary information as needed in the ViewData Dictionary. This technique allows the domain model object to be be made available in a strongly typed manner within the view, and to use model binding to put the object back together following form posts for create and update methods.

Certainly for the scale of application I've built so far this works just fine. In cases where I want to use additional information passed in the loosely typed ViewData dictionary, I'm able to cast to a typed variable at the top of the view in order to use it in a strongly typed manner from then on. And if I've needed to pass additional information back from my form posts, these can be added as additional parameters to the controller action set up to receive the post.

The following code illustrates this. The first method retrieves a User object and passes it in a strongly typed manner to the view, along with a select list for the different options for the user's Role. On the view, an additional field to indicate whether to send out a welcome email to the user can be checked. This isn't part of the model (it's not a field that needs to be persisted back to the database) and so it is passed as an additional parameter to the second method that responds to the form post.


    1 public class UserController : Controller


    2 {


    3     private UserService userService = new UserService();


    4     public ActionResult Edit(int id)


    5     {


    6         //Get user


    7         User user = userService.GetUser(id);


    8 


    9         //Get list of roles


   10         IList<Role> roles = userService.GetRoles();


   11 


   12         //Add list of roles to ViewData dictionary


   13         ViewData["RoleList"] = new SelectList(roles, "Id", "Name", user.Role.Id);


   14 


   15         //Pass the user as the model to the strongly typed view


   16         return View(user);


   17     }


   18 


   19     [AcceptVerbs(HttpVerbs.Post)]


   20     public ActionResult Edit(User user, bool sendEmail)


   21     {


   22         if (ModelState.IsValid)


   23         {


   24             //Save user details


   25             userService.SaveUser(user);


   26 


   27             //Send email flag set, so send welcome email


   28             if (sendEmail)


   29                 userService.SendWelcomeEmail(user);


   30             return RedirectToAction("Index");


   31         }


   32         else


   33         {


   34             IList<Role> roles = userService.GetRoles();


   35             ViewData["RoleList"] = new SelectList(roles, "Id", "Name", user.Role.Id);


   36             return View(user);


   37         }


   38     }


   39 }




It's clear though that in some ways these techniques aren't ideal, and it could be argued are really workarounds for not having a model that perfectly represents the data that the view is intended to display. In the example above it's really only a minor part of the view's data that doesn't come from the domain model, but in other cases the situation can be more obvious (perhaps you want a page that displays lists of two model objects - only one of which can be selected as the type for the strongly-typed view).

Which is where View Models come in. View Models aren't part of the Domain Model, and instead are created from them via composition or mapping. They can be designed to present only the information that the view requires, in a strongly typed manner.

There are a number of transformations you might want to carry out on the Domain Model object that has been designed to precisely represent the business entity or process, and the View Model object that you wish to pass to the view:
  • Additional Information - as noted above, you will often need to pass additional details to the view (e.g. to populate drop-down lists or breadcrumb trails), which can be made part of the view model.
  • Simplification - you may choose to present only the sub-set of fields that are required for display or to flatten the model
  • Logic - the view model could implement some custom logic on the base fields (e.g. presenting a calculated value to the view)
  • Formatting - the output of the view will eventually be a string (usually as part of an HTML response), and hence for consistency and in order to follow the policy of keeping the views as "dumb" as possible, you could carry out your date and number field formatting in order to present a pre-formatted string field to the view.

So sounds ideal... but of course the downside here is that it creates more work. Instead of simply passing the Domain Model object to the view and reinstantiating it using the model binder for data updates, it is necessary in convert between view and domain model and vice versa. And if in most cases there aren't hugely persuasive factors for implementation it can be that this extra layer, which can be fairly tedious and error-prone to code, doesn't appear to add too much value for the effort required.

View Model Techniques

In reading around on the various techniques for implementing view models the most useful and recommended information I found was on Steve Michelotti's blog and his post on ASP.NET MVC View Model Patterns. He distinguishes between two methods of creating view models - using composition and mapping.

The first using composition is where the Domain Model object is effectively contained by a View Model object, with the former being accessed via a field of the latter. Additional fields can be added to represent the extra information that needs to be passed to a view. The following code illustrates this idea based on the User Domain Model object described above.


    1 public class UserView


    2 {


    3     public User User { get; set; }


    4     public SelectList Roles { get; set; }


    5     public bool SendEmail { get; set; }


    6 }




    1 public ActionResult EditVM1(int id)


    2 {


    3     //Get user


    4     User user = userService.GetUser(id);


    5 


    6     //Get list of roles


    7     IList<Role> roles = userService.GetRoles();


    8 


    9     //Create view model


   10     UserView userView = new UserView();


   11     userView.User = user;


   12     userView.Roles = new SelectList(roles, "Id", "Name", user.Role.Id);


   13 


   14     //Pass the user as the model to the strongly typed view


   15     return View(userView);


   16 }


   17 


   18 [AcceptVerbs(HttpVerbs.Post)]


   19 public ActionResult EditVM1(UserView userView)


   20 {


   21     if (ModelState.IsValid)


   22     {


   23         //Save user details


   24         userService.SaveUser(userView.User);


   25 


   26         //Send email flag set, so send welcome email


   27         if (userView.SendEmail)


   28             userService.SendWelcomeEmail(userView.User);


   29         return RedirectToAction("Index");


   30     }


   31     else


   32     {


   33         return View(userView);


   34     }


   35 }




On the plus side it can be seen that this technique is relatively straightforward to implement and doesn't require too much additional work. However whilst it achieves the aim of providing all information to the view in a strongly typed manner, it doesn't do anything for the other potential benefits of simplification, logic or formatting.

The other technique is to implement a completely separate View Model class designed purely for the purposes of representing the particular data required for display in the view. Here the fields are mapped from the Domain Model object before presentation to the view. This code sample illustrates how this can be done for display of information in a form and it's subsequent processing after the post.


    1 public class UserView2


    2 {


    3     public int Id { get; set; }


    4     public string FirstName { get; set; }


    5     public string LastName { get; set; }


    6     public string Email { get; set; }


    7     public Role Role { get; set; }


    8 


    9     public SelectList Roles { get; set; }


   10     public bool SendEmail { get; set; }


   11 }





    1 public ActionResult EditVM2(int id)


    2 {


    3     //Get user


    4     User user = userService.GetUser(id);


    5 


    6     //Get list of roles


    7     IList<Role> roles = userService.GetRoles();


    8 


    9     //Create view model


   10     UserView2 userView = new UserView2();


   11     userView.Id = user.Id;


   12     userView.FirstName = user.FirstName;


   13     userView.LastName = user.LastName;


   14     userView.Email = user.Email;


   15     userView.Role = user.Role;           


   16     userView.Roles = new SelectList(roles, "Id", "Name", user.Role.Id);


   17 


   18     //Pass the user as the model to the strongly typed view


   19     return View(userView);


   20 }


   21 


   22 [AcceptVerbs(HttpVerbs.Post)]


   23 public ActionResult EditVM2(UserView2 userView)


   24 {


   25     if (ModelState.IsValid)


   26     {


   27         //Save user details


   28         User user = new User();


   29         user.Id = userView.Id;


   30         user.FirstName = userView.FirstName;


   31         user.LastName = userView.LastName;


   32         user.Email = userView.Email;


   33         user.Role = userView.Role;   


   34         userService.SaveUser(user);


   35 


   36         //Send email flag set, so send welcome email


   37         if (userView.SendEmail)


   38             userService.SendWelcomeEmail(user);


   39         return RedirectToAction("Index");


   40     }


   41     else


   42     {


   43         return View(userView);


   44     }


   45 }




Enter Automapper...

Kind of ugly? It's certainly fairly tedious code to write mapping each of the properties - particularly when there's actually not a whole lot of difference between the object fields. At the very least we would want to factor out the property-to-property mapping from our controller to a supporting builder class, but nonetheless we would still have to handle to left/right transformation of the properties.

In these cases it's worth considering the use of Automapper - an object-to-object mapper that operates on the basis of conventions.

Using this the code can be simplified so long as you follow a few simple (and sensible) conventions in the design of your View Model fields.

The first step is to download the dll from the link above and add a reference to it from the application. Mappings only need to be created once in the application lifecycle, and hence you can do this in the global.asax file:


    1 protected void Application_Start()


    2 {


    3     RegisterRoutes(RouteTable.Routes);


    4     Mapper.CreateMap<User, UserView2>();


    5 }




To create a map between objects you call the Map method, indicating the source and destination types:


    1 public ActionResult EditVM3(int id)


    2 {


    3     //Get user


    4     User user = userService.GetUser(id);


    5 


    6     //Get list of roles


    7     IList<Role> roles = userService.GetRoles();


    8 


    9     //Create view model


   10     UserView2 userView = Mapper.Map<User, UserView2>(user);


   11     userView.Roles = new SelectList(roles, "Id", "Name", user.Role.Id);


   12 


   13     //Pass the user as the model to the strongly typed view


   14     return View(userView);


   15 }


   16 


   17 [AcceptVerbs(HttpVerbs.Post)]


   18 public ActionResult EditVM3(UserView2 userView)


   19 {


   20     if (ModelState.IsValid)


   21     {


   22         //Save user details


   23         User user = Mapper.Map<UserView2, User>(userView);


   24         userService.SaveUser(user);


   25 


   26         //Send email flag set, so send welcome email


   27         if (userView.SendEmail)


   28             userService.SendWelcomeEmail(user);


   29         return RedirectToAction("Index");


   30     }


   31     else


   32     {


   33         return View(userView);


   34     }


   35 }




Because the fields have the same name, Automapper can on the basis of conventions carry out the property-to-property mapping without having to explicity code this on a per field basis.

Conclusion

Generally I'm keen to ensue that an application code-base is internally consistent, which would mean if using View Models I would like to do so throughout, and to implement them using the same technique. And hence I'm a little reluctant at this stage to take this additional step on with the applicatons I am building - for most examples I look at, the benefit doesn't as yet seem to to outweigh the cost.

Whilst I can see the value and appreciate the best practice of their use with the mapped custom model technique, for now I expect to stick with the ViewData dictionary, but will be certainly be giving them due consideration as I take on new projects and perhaps larger projects using MVC.

UPDATE 15th April: further thought and work has lead me to change my conclusion on this...

Monday, 23 November 2009

Count Active Sessions in ASP.Net

In order to monitor load on a site I've built and support, I wanted to add a simple counter to show how many active sessions were currently being handled by the web application.

The first technique was to use a counter variable held in the Application context. This was pretty straightforward - in global.asax increment the counter in the Session_Start event and decrement in the Session_End:


  1   Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)


  2 


  3       'Increment session count


  4       Application.Lock()


  5       Application("SessionCount") += 1


  6       Application.UnLock()


  7 


  8   End Sub


  9 


   10   Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)


   11 


   12       'Decrement session count


   13       Application.Lock()


   14       Application("SessionCount") = Application("SessionCount") - 1


   15       Application.UnLock()


   16 


   17   End Sub




Unfortunately this didn't turn out to be accurate in my case as I was using out of process session state, using State Server. This is a method of using an external service for the management of sessions, which is often a good idea as it means that user's session information will survive application restarts caused by uploading new .dlls or modifying the web.config file.

So instead made use of a performance counter, reading it's value in code for display in a web page:


  1   Private Sub DisplaySessionCount()


  2       Dim pc As PerformanceCounter


  3       Dim dblSessions As Double = 0


  4       If PerformanceCounterCategory.CounterExists("State Server Sessions Active", "ASP.NET") Then


  5           pc = New PerformanceCounter("ASP.NET", "State Server Sessions Active")


  6           dblSessions = pc.NextValue()


  7       End If


  8       lblSessionCount.Text = dblSessions.ToString()


  9   End Sub




There's a gotcha in that as it stands this will lead to a security exception. To avoid you need to add the website account (e.g. IUSR_MACHINENAME if IIS anonymous access and identity impersonate are being used), to the Performance Monitor and Perfomance Log groups under Computer Management > Local Users and Groups.

Tuesday, 17 November 2009

Paging with SQL Server and MySQL

Paging sets of database records in a web application efficiently has long been a relatively tricky undertaking. Whilst web form controls such as the GridView offer control based paging very easily, they will still hit the database for all the records before processing the paging within the web application, and hence unnecessary amounts of data are passed between database and web server.

Database based techniques - only retrieving the particular rows you require in each request - are therefore generally to be preferred. But some of those provide their own headaches - for example as well as retrieving the particular set of rows for a page, you will often want to know what the full count of records to be able to display a message along the lines of: 200 records found. Page 1 of 20.

SQL Server Paging

Prior to the 2005 version, I would do this using SQL Server with two queries - one to get the page of data and another, using the same WHERE clause, to get the count. With SQL Server 2005 though, you can get both with a single query, using the following syntax:

CREATE PROCEDURE getProducts
@page int,
@recordsPerPage int
AS
WITH Results AS (
SELECT ROW_NUMBER() OVER ( ORDER BY Name ) RowNumber,
ID, Name
FROM Products
)
SELECT ID,Name,
(SELECT Count(*) FROM Results) TotalRows
FROM Results
WHERE @page = 0 OR @recordsPerPage = 0 OR RowNumber BETWEEN @recordsPerPage * (@page - 1) AND @recordsPerPage * @page
ORDER BY Name

With this stored procedure, passing 0 for the page number or the records per page simply returns all of them, but with non-zero values passed only the required page of data is returned. In addition, every row will have an additional column TotalRows that returns the full count of the query.

MySQL Server Paging

On a recent project we had need to port an SQL server database to MySQL, at which point we discovered this is one of the features that isn't so easy to translate. MySQL supports the LIMIT clause in SELECT statements. It also supports a clause called SQL_CALC_FOUND_ROW which returns the total number of records returned irrespective of the LIMIT clause. These two together can be used as the basis of a similar paging technique to that described above.

The crux of the problem though is that MySQL doesn't support the use of parameters in the LIMIT clause in functions (stored procedures).

Instead you have to make use of prepared statements and execute two of them within the function to have the same result as the SQL server version. An example follows:

CREATE PROCEDURE `getProducts`(page INT,@recordsPerPage INT)
BEGIN

SET @sql = CONCAT(
"SELECT SQL_CALC_FOUND_ROWS ID,Name
FROM products
LIMIT ", page,",",@recordsPerPage,"; "
);

set @sql2 = "SELECT found_rows();";

prepare stmt from @sql;
execute stmt;

prepare stmt from @sql2;
execute stmt;

drop prepare stmt;

END;

In this case, rather than having one result set with an additional column holding the total number of rows, there will be a second result set containing just a single field where the total record count can be extracted.

In .Net the IDataReader method NextResult can be used to access this additional recordset.

Saturday, 3 October 2009

Integrating Paypal Payment Pro (Direct Payment)

I've recently had need to integrate PayPal payments on a website, using their Payments Pro interface. It's actually very straightforward, although initally didn't appear so as the information they provide on their website and forums is rather confusing. This is primarily due to the different versions of their API that have been provided historically, plus there are a number of different techniques to access it.

All I need was a simple request to authorise a set of card details, getting back a response to allow the order to either be processed appropriately.

Pulling together with my own code a few different examples online, the Paypal documentation and some advice from their technical support led me to this function - which I'll post in the hope of helping rather than hindering someone else in the same position!


    1 private void makePayPalPayment()


    2 {


    3   //Open log file


    4   TextWriter objLogFile;


    5   objLogFile = File.AppendText(ConfigurationManager.AppSettings["PaypalLogFilePath"]);


    6   try


    7   {


    8     objLogFile.WriteLine("Paypal request commenced: " + DateTime.Now.ToString());


    9 


   10     //Get details for request   


   11     // - get paypal endpoint and authorisation details


   12     string endPoint = ConfigurationManager.AppSettings["PayPalEndPoint"];


   13     string username = ConfigurationManager.AppSettings["PayPalUserName"];


   14     string password = ConfigurationManager.AppSettings["PayPalPassword"];


   15     string signature = ConfigurationManager.AppSettings["PayPalSignature"];


   16     // - get IP of current user


   17     string ip = Request.ServerVariables["REMOTE_ADDR"];


   18     // - get customer details (from form fields)


   19     string cardType = ddlCardType.SelectedItem.Value;


   20     string cardNumber = txtCardNumber.Text;


   21     string expDate = ddlExpiryDateMonth.SelectedItem.Value + ddlExpiryDateYear.SelectedItem.Value;


   22     string securityCode = txtSecurityCode.Text;


   23     string firstName = txtFirstName.Text;


   24     string lastName = txtLastName.Text;


   25     string street = txtStreet.Text;


   26     string city = txtCity.Text;


   27     string state = txtState.Text;


   28     string zip = txtZip.Text;


   29     string countryCode = ddlCountry.SelectedItem.Value;   


   30     // - get order details (hardcoded in this example)


   31     decimal orderAmount = 10.0;


   32     string currencyCode = "GBP";


   33 


   34     //Build request


   35     StringBuilder request = new StringBuilder();


   36     request.Append("VERSION=52.0");


   37     request.Append("&USER=" + HttpUtility.UrlEncode(username));


   38     request.Append("&PWD=" + HttpUtility.UrlEncode(password));


   39     request.Append("&SIGNATURE=" + HttpUtility.UrlEncode(signature));


   40     request.Append("&METHOD=DoDirectPayment");


   41     request.Append("&PAYMENTACTION=Sale");


   42     request.Append("&AMT=" + orderAmount.ToString("F"));


   43     request.Append("&IPADDRESS=" + HttpUtility.UrlEncode(ip));


   44     request.Append("&CREDITCARDTYPE=" + HttpUtility.UrlEncode(cardType));


   45     request.Append("&ACCT=" + HttpUtility.UrlEncode(cardNumber));


   46     request.Append("&EXPDATE=" + HttpUtility.UrlEncode(expDate));


   47     request.Append("&CVV2=" + HttpUtility.UrlEncode(securityCode));


   48     request.Append("&FIRSTNAME=" + HttpUtility.UrlEncode(firstName));


   49     request.Append("&LASTNAME=" + HttpUtility.UrlEncode(lastName));


   50     request.Append("&STREET=" + HttpUtility.UrlEncode(street));


   51     request.Append("&CITY=" + HttpUtility.UrlEncode(city));


   52     request.Append("&STATE=" + HttpUtility.UrlEncode(state));


   53     request.Append("&ZIP=" + HttpUtility.UrlEncode(zip));


   54     request.Append("&INVOICEID=" + HttpUtility.UrlEncode(order.ID.ToString()));


   55     request.Append("&COUNTRYCODE=" + HttpUtility.UrlEncode(countryCode));


   56     request.Append("&CURRENCYCODE=" + HttpUtility.UrlEncode(currencyCode));


   57     objLogFile.WriteLine("Request: " + request.ToString());


   58 


   59     //Set up flags for success of operation


   60     bool success = false;


   61     string errorMessage = "";


   62 


   63     //Make Paypal request


   64     HttpWebRequest webRequest = WebRequest.Create(endPoint) as HttpWebRequest;


   65     webRequest.Method = "POST";


   66     webRequest.ContentType = "application/x-www-form-urlencoded";


   67     webRequest.ContentLength = request.Length;


   68     StreamWriter writer = new StreamWriter(webRequest.GetRequestStream());


   69     writer.Write(request.ToString());


   70     writer.Close();


   71 


   72     //Get Paypal response


   73     HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();


   74 


   75     //Check status


   76     if (webRequest.HaveResponse && (webResponse.StatusCode == HttpStatusCode.OK || webResponse.StatusCode == HttpStatusCode.Accepted))


   77     {


   78       //Response received, so parse into name value pairs for processing


   79       StreamReader reader = new StreamReader(webResponse.GetResponseStream());


   80       string responseString = reader.ReadToEnd();


   81       objLogFile.WriteLine("Response: " + responseString);


   82       reader.Close();


   83       char[] ampersand = { '&' };


   84       char[] equalsign = { '=' };


   85       string[] pairs = responseString.Split(ampersand);               


   86       for (int i = 0; i < pairs.Length; i++)


   87       {


   88         string[] pair = pairs[i].Split(equalsign);


   89         //One pair will be the acknowledgement (ACK) - if this is not set to failure we know the payment has been authorised


   90         if (pair[0].ToLower() == "ack" && HttpUtility.UrlDecode(pair[1]).ToLower() != "failure")


   91         {


   92           success = true;


   93         }


   94         //One or more messages wil be returned in the case of a failure, so build up list of messages to present to user (or developer :-))


   95         if (pair[0].Length > 13 && pair[0].Substring(0,13).ToLower() == "l_longmessage")


   96         {


   97           errorMessage += "<li>" + HttpUtility.UrlDecode(pair[1]) + "</li>";


   98         }


   99       }


  100     }


  101 


  102     //Display appropriate message to user


  103     if (success)


  104     {


  105       // *** process authorised order ***


  106     }


  107     else


  108     {


  109       lblResultMessage.Text = "Sorry, but your payment transaction cannot be proceed. Please review the following messages and try again, or select another payment method.";


  110       lblResultMessage.Text += "<ul>" + errorMessage + "</ul>";


  111     }


  112   }


  113   finally


  114   {


  115     objLogFile.WriteLine("");


  116     objLogFile.Close();


  117   }


  118 }


Monday, 21 September 2009

MVC/Jquery Calendar

One of the main pluses of working with the ASP.Net MVC framework is of course the separation of concerns it promotes with the split between the controller and the view in the UI. This is not only apparent in the standard method of having the controller obtain the data required for display, and passing this to a view for display as XHTML. The same clear separation of purpose can be made use of when the output of the view is other formats too.

In a recent project I've had need to represent a calendar of events - a simple side bar to the website that displays a calendar view with days that have events associated with them highlighted. Clicking on the day displays the list of events below.

In implementing this feature I made use of the controller and view pattern in three distinct ways.
  1. Having a controller render an XHTML page view
  2. Having a controller render JSON data for use from an AJAX request.
  3. Having a controller render a partial view to support an AJAX page update.
Application Structure

The back-end of this MVC application consisted of a database table of events, accessed via a stored procedure and an ADO.Net repository layer. My controller made calls to obtain data in the form of custom business entities from this repository layer via a service layer.

The controller consists of three methods as illustrated in listing 1.

Listing 1: Controller methods


    1  /// <summary>


    2  /// Renders calendar partial view


    3  /// </summary>


    4  /// <returns></returns>


    5  public ActionResult Calendar()


    6  {


    7      return View();


    8  }


    9 


   10  /// <summary>


   11  /// Renders events for month in JSON format (for AJAX page injection)


   12  /// </summary>


   13  /// <param name="year">Year</param>


   14  /// <param name="month">Month</param>


   15  /// <returns></returns>


   16  public JsonResult ListAsJson(int year, int month)


   17  {


   18      IList<Event> events = eventsService.GetEventList(year, month, 0);


   19      return this.Json(events);


   20  }


   21 


   22  /// <summary>


   23  /// Returns partial view with events for selected day (for AJAX page injection)


   24  /// </summary>


   25  /// <param name="year">Year</param>


   26  /// <param name="month">Month</param>


   27  /// <param name="day">Day</param>


   28  /// <returns></returns>


   29  public ActionResult EventsForDay(int year, int month, int day)


   30  {


   31      IList<Event> events = eventsService.GetEventList(year, month, day);


   32      return View(events);


   33  }




The first, Calendar(), is pretty much as a simple as a controller method gets. Initially I had this retrieving the event data and passing it to the view, but when I realised I wanted to go the AJAX route for display as the calendar is paged from month to month, I decided not to retrieve any data for the initial display. So this simply renders the view.

As the calendar is displayed on most pages of the website, it's called via a RenderAction method coded into the site's master page.

The second ListAsJson() retrieves the events for the supplied year and month and renders them in JSON format. This format, which is native JavaScript of course, can be used directly in the client side code that makes an AJAX call as the months are selected in the calendar.

Finally, EventsForDay() similarly retrieves event data, this time for a specified day. The event data is supplied to a partial view which consists of not a whole XHTML template, but just a fragment. This is then injected into the page using JavaScript.

Listing 2: View XHTML


    1 <div id="calendar-render"></div>


    2 


    3 <script src="<%= Url.Content("~/js/calendar.js") %>" type="text/javascript"></script>


    4 


    5 <div id="calendar-events-display">


    6     <h3></h3>   


    7     <div id="calendar-events-display-events"></div>


    8 </div>




As the major part of the work is via AJAX calls from JavaScript, the view is very simple. Really it just contains a couple of placeholders for the calendar control and event list to be rendered.

Listing 3: JavaScript (jquery) AJAX code


    1 var selectableDates;


    2 var months = new Array(13);


    3 months[0] = "January";


    4 months[1] = "February";


    5 months[2] = "March";


    6 months[3] = "April";


    7 months[4] = "May";


    8 months[5] = "June";


    9 months[6] = "July";


   10 months[7] = "August";


   11 months[8] = "September";


   12 months[9] = "October";


   13 months[10] = "November";


   14 months[11] = "December";


   15 


   16 //Hook-up the jquery datepicker


   17 $(document).ready(function() {


   18     var today = new Date();


   19     getEventsForMonth(today.getUTCFullYear(), today.getMonth() + 1);


   20 });


   21 


   22 //Set up the datepicker


   23 function initializeDatePicker(startDate) {


   24 


   25     //Check if current month - if so use current day rather than 1st of the month


   26     var today = new Date();


   27     if (startDate.getUTCFullYear() == today.getUTCFullYear() && startDate.getMonth() == today.getMonth())


   28         startDate = today;


   29 


   30     $("div#calendar-render").datepicker('destroy'); // Destroy if previously created so render will call again when events wired up


   31     $("div#calendar-render").datepicker({


   32         defaultDate: startDate,


   33         beforeShowDay: renderDay,


   34         onSelect: showEvents


   35     }).datepicker("option", { onChangeMonthYear: getEventsForMonth });


   36 }


   37 


   38 //Render a day as selectable if there's an event on that day


   39 function renderDay(date) {


   40     return [isDateSelectable(date), ""];


   41 }


   42 


   43 //Checks to see if date to be rendered is in the list of selectable dates


   44 function isDateSelectable(date) {


   45     for (var i = 0; i < selectableDates.length; i++) {


   46         if (selectableDates[i].getDate() == date.getDate())


   47             return true;


   48     }


   49     return false;


   50 }


   51 


   52 //Makes AJAX call to get events list for month and populates the selectableDates array


   53 function getEventsForMonth(year, month, inst) {


   54     $("div#calendar-events-display").hide();


   55     selectableDates = new Array();


   56     $.getJSON("/Events/ListAsJson?year=" + year + "&month=" + month, null, function(data) {


   57         var i = 0;


   58         $.each(data, function(index, evt) {


   59             selectableDates[i] = extractDateFromJSONResponse(evt.Start);


   60             i++;


   61         });


   62         initializeDatePicker(new Date(year, month - 1, 1));


   63     });


   64 }


   65 


   66 //Extracts date object from JSON formatted date response


   67 function extractDateFromJSONResponse(jsonDate) {


   68     return new Date(parseInt(jsonDate.substr(jsonDate.indexOf("/Date(") + 6, jsonDate.length - 8)));


   69 }


   70 


   71 //Show events for selected day


   72 function showEvents(dateText, inst) {


   73     var d = new Date(dateText);


   74     var day = d.getDate();


   75     var month = d.getMonth() + 1;


   76     var year = d.getUTCFullYear();


   77     $("div#calendar-events-display").hide();


   78     $("div#calendar-events-display h3").html(day + " " + months[month - 1] + " " + year);


   79     $.get("/Events/EventsForDay?year=" + year + "&month=" + month + "&day=" + day, null, function(data, textStatus) {


   80         $("div#calendar-events-display-events").html(data);


   81         $("div#calendar-events-display-events div:nth-child(odd)").addClass("alt");


   82         $("div#calendar-events-display-events div div:nth-child(odd)").removeClass("alt");


   83         $("div#calendar-events-display").show("fast");


   84     });       


   85 }




The JavasScript code itself makes use of the Jquery UI DatePicker that can be custom styled to provide a very neat client side calendar control. It provides events such as for day rendering and navigating from month to month, that I've hooked into in order to make the AJAX requests to retrieve the events for each month, and for rendering the listing of events for a day when a particular date is selected.