Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

Thursday, February 10, 2011

Mocking the jQuery Ajax Call in ASP.NET MVC 3 Music Store

Sometimes the front-end and back-end of your application progress at different speeds. In order for each layer to progress independently it is optimal if the front-end piece can mock the results of the back-end.

I have grabbed the AJAX section from the recently updated ASP.NET MVC 3 Music Store and I slightly tweaked it below.

$(".RemoveLink").click(function() {
  var recordToDelete = $(this).attr("data-id");

  if (recordToDelete) {
    $.post("/ShoppingCart/RemoveFromCart", {
      "id": recordToDelete
    }, function(data) {
      if (data.ItemCount === 0) {
        $('#row-' + data.DeleteId).fadeOut('slow');
      } else {
        $('#item-count-' + data.DeleteId)
          .text(data.ItemCount);
      }

      $('#cart-total').text(data.CartTotal);
      $('#update-message').text(data.Message);
      $('#cart-status')
        .text('Cart (' + data.CartCount + ')');
    });
  }
});    

Let's say for example, that the controller action wasn't yet implemented, but the front-end developer still wanted to progress in their code. To do this we are going to introduce a new tool called Mockjax to simulate a response from a jQuery ajax call.

The following code mocks any call to the /ShoopingCart/RemoveFromCart URL and will respond with the following object literal.

$.mockjax({
  url: "/ShoppingCart/RemoveFromCart",
  responseTime: 750,
  responseText: {         
    ItemCount: 5,
    DeleteId: 1,
    CartTotal: 112.45,
    Message: "Your cart has been updated!",
    CartCount: 16
  }
});

The great thing about the above technique is that you are guaranteed the same response will be returned no matter what request you made to the ajax call. For example, this approach can be very good for Unit Testing. However, if you are trying to demonstrate the code to a client or just want to play around with the behavior then you might want something a little more flexible and dynamic.

Fortunately, Mockjax not only allows you to return a static set of responseText or repsonseXML, but it also lets you dynamically decide what content you want returned, which is what we are going to demonstrate below.
Note: Mockjax offers much more control than what I am showing in this example. I encourage you to check out all of it's features.
$.mockjax({
  url: "/ShoppingCart/RemoveFromCart",
  responseTime: 750,
  response: function(settings) {
    var row = $("#row-" + settings.data.id),
      itemCount = +row.find("td").eq(2)
        .text(function(index, text) {
          return +$.trim(text) - 1;
        }).text(),
      cartTotal = 0, cartCount = 0;
        
    $("table tr").each(function(index, element) {
      var row = $(element),
        price = +row.find("td").eq(1).text(),
        quantity = +row.find("td").eq(2).text();

      if (price && quantity) {
        cartCount += quantity;
        cartTotal += price * quantity;
      }
    });

    this.responseText = {         
      ItemCount: itemCount,
      DeleteId: settings.data.id,
      CartTotal: cartTotal.toFixed(2),
      Message: "Your cart has been updated!",
      CartCount: cartCount
    };
  }
});

This might seem like excessive front-end code, but the intent is to simulate what the back-end might return in order for the front-end to respond in an appropriate manner. The code is doing some basic calculations to determine what the Shopping Cart quantity and values might be after deleting an item.
Note: The mockjax response code above could have been simplified somewhat if I had modified the MVC 3 View rendering the HTML, but as in many projects you don't always have access to change the existing output. I decided to go with that approach instead of changing the MVC 3 View code in this blog post.

Once the back-end code is complete, then you can remove the $.mockjax call from your code-base and it will be hitting your back-end resource instead of the mocked response.

I captured the HTML, CSS, and JavaScript from the MVC 3 Music Store and put it in the following jsFiddle for you to execute and play around with. The main piece that I added was the mockjax code to simulate the AJAX request and response from the server.


Note: You might also consider mockjax to be a great Unit Testing tool as you are developing your front-end code and the back-end isn't yet implemented.


Thursday, July 01, 2010

jQuery jqGrid Plugin: Add, Edit, Delete with ASP.NET MVC

 

Introduction

There are a lot of articles out there that show you how to integrate with the jQuery jqGrid Plugin from a listing, paging, sorting approach, but I haven’t seen many that show the integration with the add/edit/delete features that jqGrid offers.

It turns out that it isn’t very difficult to do, but it took quite a bit of digging in the jqGrid API documentation for me to find all the things I needed.

The following article will show how to customize the add/edit/delete modal experience inside of the jqGrid with ASP.NET MVC.

Contact ViewModel

First, you start off with your ViewModel. In this case the following is really bare bones. I haven’t annotated the properties with metadata because I actually do that manually in the jqGrid columns. That isn’t optimal, but it would be nice to have these automatically mapped. That sounds like another blog post ;)

public class ContactViewModel
{
    public System.Guid ContactId { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public string PhoneNumber { get; set; }

    public DateTime DateOfBirth { get; set; }

    public bool IsMarried { get; set; }
}

Contact View

The following code setups up the jqGrid to support add, edit, and delete.

The first function you’ll see is a custom validator that checks to see if the phone number has a length of 14. Yes, it isn’t bullet-proof validation by any stretch of the imagination, but its more of an example of what can be done.

Next you’ll see the updateDialog object literal defining the look and behavior of the add, edit, and delete dialog windows. The main property to define is the URL where the AJAX requests will post the data. You can also control whether the dialog closes immediately, if it’s a modal dialog, etc…

The next important thing to notice is the “key: true” property of the ContactId column. If you don’t set this property then the POST for the delete command  only send the relative ID that jqGrid generates, not the ContactId that you need. So, this is important ;)

Note: You’ll see some code below setting global properties for the jqGrid such as the title of the dialogs, buttons, etc. If you don’t do this then you’ll get generic titles. I figured these customizations made the user experience a little nicer, but things will work just find without them.

function isValidPhone(value, name) {
    console.log('isValidPhone');
    var errorMessage = name + ': Invalid Format';
    var success = value.length === 14;
    return [success, success ? '' : errorMessage];
}  

$(document).ready(function () {
    var updateDialog = {
        url: '<%= Url.Action("Update", "Contact") %>'
        , closeAfterAdd: true
        , closeAfterEdit: true
        , afterShowForm: function (formId) {
            $("#PhoneNumber").mask("(999) 999-9999");
            $("#DateOfBirth").datepicker();
        }
        , afterclickPgButtons: function (whichbutton, formid, rowid) {
            $("#PhoneNumber").mask("(999) 999-9999");
        }
        , modal: true
        , width: "400"
    };

    $.jgrid.nav.addtext = "Add";
    $.jgrid.nav.edittext = "Edit";
    $.jgrid.nav.deltext = "Delete";
    $.jgrid.edit.addCaption = "Add Contact";
    $.jgrid.edit.editCaption = "Edit Contact";
    $.jgrid.del.caption = "Delete Contact";
    $.jgrid.del.msg = "Delete selected Contact?";

    $("#list").jqGrid({
        url: '<%= Url.Action("List", "Contact") %>',
        datatype: 'json',
        mtype: 'GET',
        colNames: ['ContactId', 'Name', 'Date of Birth', 'E-mail', 'Phone Number', 'Married'],
        colModel: [
            { name: 'ContactId', index: 'ContactId', width: 40, align: 'left', key: true, editable: true, editrules: { edithidden: false }, hidedlg: true, hidden: true },
            { name: 'Name', index: 'Name', width: 300, align: 'left', editable: true, edittype: 'text', editrules: { required: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'DateOfBirth', index: 'DateOfBirth', width: 200, align: 'left', formatter: 'date', datefmt: 'm/d/Y', editable: true, edittype: 'text', editrules: { required: true, date: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'Email', index: 'Email', width: 200, align: 'left', formatter: 'mail', editable: true, edittype: 'text', editrules: { required: true, email: true }, formoptions: { elmsuffix: ' *'} },
            { name: 'PhoneNumber', index: 'PhoneNumber', width: 200, align: 'left', editable: true, edittype: 'text', editrules: { required: true, custom: true, custom_func: isValidPhone }, formoptions: { elmsuffix: ' *'} },
            { name: 'IsMarried', index: 'IsMarried', width: 200, align: 'left', editable: true, edittype: 'checkbox', editoptions: { value: "True:False" }, editrules: { required: true}}],
        pager: $('#listPager'),
        rowNum: 1000,
        rowList: [1000],
        sortname: 'ContactId',
        sortorder: "desc",
        viewrecords: true,
        imgpath: '/Content/Themes/Redmond/Images',
        caption: 'Contact List',
        autowidth: true,
        ondblClickRow: function (rowid, iRow, iCol, e) {
            $("#list").editGridRow(rowid, prmGridDialog);
        }
    }).navGrid('#listPager',
        {
            edit: true, add: true, del: true, search: false, refresh: true
        },
        updateDialog,
        updateDialog,
        updateDialog
    );
}); 

Contact Controller Update Action

The add/update/delete feature takes one URL where you can change the logic based on the operation type. The MVC Modal Binder will map the fields into your ViewModel in most cases. The exception is the “id” that is passed on the delete operation, but there is a way to get around that later in this post ;)

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
    var operation = formCollection["oper"];
    if (operation.Equals("add") || operation.Equals("edit"))
    {
        repository.SaveOrUpdate(new ContactViewModel
        {
            ContactId = viewModel.ContactId,
            DateOfBirth = viewModel.DateOfBirth,
            Email = viewModel.Email,
            IsMarried = viewModel.IsMarried,
            Name = viewModel.Name,
            PhoneNumber = viewModel.PhoneNumber
        });
    }
    else if (operation.Equals("del"))
    {
        repository.Delete(new ContactViewModel
        {
            ContactId = new Guid(formCollection["id"])
        });
    }

    return Content(repository.HasErrors.ToString().ToLower()); 
}

What About Using Complex Keys?

Instead of having ContactId (“key: true”) as your key to delete, you might have a more complex key to identify which item to delete. As it turns out, you can bind to the onclickSubmit event of the add/edit/delete dialog and change what data is POST’ed to the controller.

A nice side effect of this is that you name your property such that the MVC Modal Binder works.

Updated Dialog Object Literal

var updateDialog = {
    url: '<%= Url.Action("Update", "Contact") %>'
    , closeAfterAdd: true
    , closeAfterEdit: true
    , afterShowForm: function (formId) {
        $("#PhoneNumber").mask("(999) 999-9999");
        $("#DateOfBirth").datepicker();
    }
    , afterclickPgButtons: function (whichbutton, formid, rowid) {
        $("#PhoneNumber").mask("(999) 999-9999");
    }
    , modal: true
    , onclickSubmit: function (params) {
        var ajaxData = {};

        var list = $("#list");
        var selectedRow = list.getGridParam("selrow");
        rowData = list.getRowData(selectedRow);
        ajaxData = { ContactId: rowData.ContactId };

        return ajaxData;
    }
    , width: "400"
};

Updated Contact Controller Update Action

public ActionResult Update(ContactViewModel viewModel, FormCollection formCollection)
{
    var operation = formCollection["oper"];
    if (operation.Equals("add") || operation.Equals("edit"))
    {
        repository.SaveOrUpdate(new ContactViewModel
        {
            ContactId = viewModel.ContactId,
            DateOfBirth = viewModel.DateOfBirth,
            Email = viewModel.Email,
            IsMarried = viewModel.IsMarried,
            Name = viewModel.Name,
            PhoneNumber = viewModel.PhoneNumber
        });
    }
    else if (operation.Equals("del"))
    {
        repository.Delete(new ContactViewModel
        {
            ContactId = viewModel.ContactId
        });
    }

    return Content(repository.HasErrors.ToString().ToLower()); 
}

Conclusion

I hope you found the above article of some use in your everyday coding. The jqGrid also provides an inline editing feature similar to what you might experience in an Excel grid. You might look into that if you are interested.

Please give me your feedback. Thanks!

Download Source Code



Monday, June 21, 2010

BDD-Style QUnit Testing ASP.NET MVC’s jQuery Validation

Client-Side Unit Testing



The goal of this blog post is to show how you can utilize some helpful techniques to easily Unit Test your Web Application. In this post I will focus on Unit Testing the Client-Side validation rules that ASP.NET MVC generates. You can apply the following techniques to pretty much any scenario, but since this is something I do, I thought I’d share.

Our sample applications is a Contact Manager. At this point we only have a toolbar with a “New Contact” button. When the button is clicked a “New Contact” dialog will appear with several input fields and a “Save” and “Cancel” button. All of the fields are required, so if the user clicks the “Save” button client-validation should verify that all fields have a value.

ASP.NET MVC Contact ViewModel


First lets take a look at our ViewModel which will drive the rules of our client-side validation.

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace jQueryUnitTestingFormValidation.Models
{
    public class ContactViewModel
    {
        [HiddenInput(DisplayValue = false)]
        public System.Guid Id { get; set; }

        [Required(ErrorMessage = "Name Required")]
        [DisplayName("Name")]
        [StringLength(50, ErrorMessage = "Name must be less than or equal to 50 characters")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Email Required")]
        [DisplayName("E-mail")]
        [StringLength(50, ErrorMessage = "Email must be less than or equal to 50 characters")]
        [DataType(DataType.EmailAddress)]
        [RegularExpression(@"^([a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\s*;?\s*)+$", ErrorMessage = "Email must be valid")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Phone Number Required")]
        [DisplayName("Phone Number")]
        [StringLength(50, ErrorMessage = "Phone must be less than or equal to 50 characters")]
        public string PhoneNumber { get; set; }

        [Required(ErrorMessage = "Date of Birth Required")]
        [DisplayName("Date of Birth")]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "Required")]
        [DisplayName("Is Married")]
        public bool IsMarried { get; set; }
    }
}

ASP.NET MVC Contact View


Our View is pretty simple. I decided for this sample to not modify the MasterPages that the Templated Helpers use. You’ll see that I’m call the LabelFor, EditorFor, and ValidationMessageFor and organizing them as I wish. If you are interested in a cleaner way to do this you can check out a previous blog entry I wrote entitled Opinionated ASP.NET MVC 2 Template Helpers.

<div id="createDialog" title="New Contact" style="display: none;">
    <% Html.EnableClientValidation(); %> 
    <% using (Html.BeginForm("Create", "Contact", FormMethod.Post, new {@id = "createPost"})) { %>
        <dl>
            <dt><%= Html.LabelFor(m => m.Name) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.Name) %>
                <%= Html.ValidationMessageFor(m => m.Name) %>
            </dd>
            <dt><%= Html.LabelFor(m => m.Email) %></dt>            
            <dd>
                <%= Html.EditorFor(m => m.Email) %>
                <%= Html.ValidationMessageFor(m => m.Email)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.PhoneNumber) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.PhoneNumber) %>
                <%= Html.ValidationMessageFor(m => m.PhoneNumber)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.DateOfBirth) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.DateOfBirth) %>
                <%= Html.ValidationMessageFor(m => m.DateOfBirth)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.IsMarried) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.IsMarried) %>
                <%= Html.ValidationMessageFor(m => m.IsMarried)%>
            </dd>            
        </dl>
    <% } %>    
</div>

JavaScript Contact Revealing Module


The Contact Revealing Module contains the logic to initialize the button and dialog events, post the form to the Controller, etc…

Note: I am utilizing the Revealing Module Pattern for those of you who might not be aware of it. It is very helpful in splitting up your JavaScript code into testable and reusable modules.

var contactCreateModule = (function () {
    var public = {},
        dialogWidth = 800;

    public.createDialog;
    public.createPost;

    public.init = function () {
        public.createDialog = $("#createDialog");
        public.createPost = $("#createPost");

        public.initEventHandlers();
    };

    public.initEventHandlers = function () {
        public.initToolbar();

        public.initDialog();
    };

    public.initToolbar = function () {
        $("#toolbar button").button();

        $("#createContact").click(function () {
            public.displayCreate();
        });
    };

    public.initDialog = function () {
        $(".datePicker").datepicker();

        public.createDialog.dialog({
            autoOpen: false
            , width: dialogWidth
            , modal: true
            , open: validationModule.clearValidationMessages
            , buttons: {
                "Cancel": function () {
                    public.createDialog.dialog("close");
                },
                "Save": function () {
                    if (public.createPost.valid()) {
                        public.createDialog.dialog("close");
                        public.postContact();
                    }
                }
            }
        });
    };

    public.displayCreate = function () {
        public.createDialog
            .dialog("open")
            .find("input:first")
            .focus();
    };

    public.postContact = function (callback) {
        $.ajaxSettings.traditional = true;
        $.ajax({
            url: public.createPost.attr("action"),
            data: public.createPost.serialize(),
            type: "POST",
            success: function (data, textStatus, xhr) {
                public.postContactSuccess(data, textStatus, xhr);
                callback(data && data.Success);
            },
            error: public.postContactError
        });
    };

    public.postContactSuccess = function (data, textStatus, xhr) {
        if (data && data.Success) {
            notificationModule.displayMessage(true, "Your contact has been created!");
        } else {
            notificationModule.displayMessages(data.Success, data.Messages);
        }
    };

    public.postContactError = function (xhr, textStatus, error) {
        var errorMessage = exception ? exception : xhr.statusText;
        notificationModule.displayMessage(false, "There was an error creating your contact: " + errorMessage);
    };

    return public;
} ());

Classic QUnit Style Tests


I initially started this blog post using standard QUnit syntax, but there was something about it that didn’t stand well with me. In particular, I didn’t like how I had a bunch of asserts all lumped together. Later in this article I switch out the classic QUnit-Style with a BDD-Style syntax.

The following are some screenshots from the Unit Tests…


The following is a slightly zoomed in view of the above image. You can see that all the asserts for one particular test are hidden beneath it. You can expand &| collapse the test to see each individual assert.


You can view the unit tests below that generated the above screen shots.
Since I don’t want the $.ajax call to actually occur in my Unit Tests, I swap out the default functionality with a stub function instead in the module setup (which is called before each test). In the module teardown (which is called after each test) I restore the default functionality in case any future test needs it.

var contactWasPosted = false;
module("Contact: Create", {
    setup: function () {
        contactWasPosted = false;

        contactCreateModule.postContactBackup = contactCreateModule.postContact;
        contactCreateModule.postContact = function (callback) {
            contactWasPosted = true;
        };
    },
    teardown: function () {
        contactCreateModule.postContact = contactCreateModule.postContact;
        contactCreateModule.createDialog.dialog('close');
    }
});

test("When New Contact Button Clicked", function () {
    //Arrange

    //Act
    $("#createContact").click();

    //Assert
    ok($("#createDialog:visible").length, "Dialog Should Display");
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
});

test("When Click Save On an Empty Form", function () {
    //Arrange
    $("#createContact").click();

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:visible").length, "Name Validation Should Display");
    ok($("#Email_validationMessage:visible").length, "Email Validation Should Display");
    ok($("#PhoneNumber_validationMessage:visible").length, "PhoneNumber Validation Should Display");
    ok($("#DateOfBirth_validationMessage:visible").length, "DateOfBirth Validation Should Display");
    ok($("#IsMarried_validationMessage:visible").length, "IsMarried Validation Should Display");
    ok($("#createDialog:visible").length, "Dialog Should Remain Displayed");
});

test("When Click Save On an Complete Form", function () {
    //Arrange
    $("#createContact").click();

    $("#Name").val("xNamex");
    $("#Email").val("tasty@bacon.com");
    $("#PhoneNumber").val("xPhoneNumberx");
    $("#DateOfBirth").val("xDateOfBirthx");
    $("#IsMarried").attr("checked", true);

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
    ok(contactWasPosted, "Contact Should Post");
    ok($("#createDialog:not(:visible)").length, "Dialog Should Be Closed");
});

BDD Style QUnit Tests


After talking more with Dan Mohl (@dmohl) I decided I wanted to try to find a Behavior Driven style of writing QUnit tests. I know there are several other BDD Client-Side Unit Test frameworks out there, but I wanted to keep to the QUnit runner for now.

So, during my research the author of Pavlov, Michael Monteleone (@namelessmike), let me know about his project, which ended up to be exactly what I was looking for.

The following is the output of my tests using QUnit and Pavlov…


Here is a slightly zoomed in view of the QUnit Pavlov test output…


The structure of the Unit Tests is dramatically different from the above classic Unit Tests.

The first thing you’ll notice is that I am extending the Assertion definitions to clean up some of the assert code that I had in my previous Unit Tests.

I still have the same logic in from the above Unit Tests that was in the setup and teardown methods, but now you can find those in the before and after methods.

The syntax of Pavlov is very readable from an English standpoint. You basically describe some scenario in words, and then split it out into code. It was very refreshing once I put it all together.

QUnit.specify.extendAssertions({
    isNotDisplayed: function(actual, expected, message) {
        ok(actual.is(":hidden") || actual.text().length == 0, message || "okay: isNotDisplayed");
    },
    isDisplayed: function (actual, expected, message) {
        ok(actual.is(":visible") || actual.text().length > 0, message || "okay: isDisplayed");
    }
});

QUnit.init({ moduleTestDelimeter: ", it " });
QUnit.specify.globalApi = true;
QUnit.specify("Contact", function () {

    describe("Create", function () {

        var contactWasPosted;

        before(function () {
            contactWasPosted = false;

            contactCreateModule.postContactBackup = contactCreateModule.postContact;
            contactCreateModule.postContact = function (callback) {
                contactWasPosted = true;
            };

            $("#createContact").click();
        });

        after(function () {
            contactCreateModule.postContact = contactCreateModule.postContact;
            contactCreateModule.createDialog.dialog('close');
        });

        describe("When the contact button is clicked", function () {
            it("should display the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });
        });

        describe("When clicking save on an empty form", function () {
            before(function () {
                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should keep the dialog displayed", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should display Name validation", function () {
                assert($("#Name_validationMessage:visible")).isDisplayed();
            });

            it("should display Email validation", function () {
                assert($("#Email_validationMessage:visible")).isDisplayed();
            });

            it("should display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isDisplayed();
            });

            it("should display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isDisplayed();
            });

            it("should display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isDisplayed();
            });
        });

        describe("When clicking save on a completed form", function () {
            before(function () {
                $("#Name").val("xNamex");
                $("#Email").val("tasty@bacon.com");
                $("#PhoneNumber").val("xPhoneNumberx");
                $("#DateOfBirth").val("xDateOfBirthx");
                $("#IsMarried").attr("checked", true);

                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });

            it("should post contact", function () {
                assert(contactWasPosted).isTrue();
            });

            it("should close the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(0);
            });
        });

    });

});

Conclusion


The more and more I find myself creating highly dynamic websites, the more I find the need to Unit Test the browser interaction.

I hope you found the above example helpful. I would be interested to hear what tools you use to help Unit Test your client-side code. Please share… it makes us all better :)
You can find some other helpful client-side Unit Testing tools in the Script Junkie article I wrote entitled jQuery Test-Driven Development. In addition I wrote several other jQuery related articles that you can find on Script Junkie.

Note: It was not my intention to exhaustively Unit Test everything in the above example. There are other things I would Unit Test, but to make this example easy to understand in a bite-sized chuck, I limited myself to some simple examples.

Download Source Code


Wednesday, May 19, 2010

Opinionated ASP.NET MVC 2 Template Helpers

If you have used ASP.NET MVC any, then you are probably aware of the MVC Contrib project hosted on CodePlex. It is a helpful library that provides useful tools that ASP.NET MVC doesn’t give you out of the box.

Opinionated Input Builders

One of the pieces that I really like is the Opinionated Input Builders that Eric Hexter wrote. The builder allows you to provide one property at a time from your View Model and it will output the label, required indicator, the appropriate input control, and whatever else that it needs to display.

<% using (Html.BeginForm()) { %>

    <%= Html.Input(m => m.FirstName) %>
    <%= Html.Input(m => m.LastName) %>
    <%= Html.Input(m => m.Email) %>

    <p class="actions">
        <input type="submit" value="Create" />
    </p>

<% } %>

 

MvcContribPicNik

ASP.NET MVC 2 EditorForModel

As it turns out, the ASP.NET MVC team took a similar approach when putting together the Template Helpers in  ASP.NET MVC 2. I ended up switching to the Template Helpers.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorForModel() %>

    <p class="actions">
        <input type="submit" value="Create" />
    </p>

<% } %>

With some CSS styling, the output of the EditorForModel is close to what the Opinionated Input Builder, but there are some problems as we’ll discuss in the next section.

Mvc2ModelPikNic

ASP.NET MVC 2 EditorFor

The problem is that I usually don’t want to display or edit my entire model. I often times have things in my View Model that I don’t particularly want to display.  I want to rather manually choose which properties I want to display or edit.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorFor(m => m.FirstName) %>
    <%= Html.EditorFor(m => m.LastName)%>
    <%-- Purposely Not Show the Email –%>

    <p class="actions"> 
        <input type="submit" value="Create" /> 
    </p> 

<% } %>

Using the above syntax doesn’t quite give the output I was looking for. Only the input controls are rendered (as seen in the following screenshot) instead of providing the scaffolding of label, input, and validation fields like the EditorForModel method provides.

Mvc2TemplatedHelperBeforePicNik

You end up having to provides the layout of the controls and explicitly mention the label, input controls, and validation for each property. 

Modify the Template Helpers MasterPage

I soon began to miss the simple syntax of rendering all the necessary code like I was used to when using the Opinionated Input Builders outputted from the MVC Contrib.

Well, a while back Brad Wilson (one of the ASP.NET MVC 2 developers), wrote an awesome series about Templated Helpers and the last post in the series, ASP.NET MVC 2 Templates, Part 5: Master Page Templates, he addressed this granular Opinionated Input Builder type syntax!

All you do, is to override the Master Page that the templates use internally. So, inside my Master Page I layout where I want the label, validation, and input controls to go and then ASP.NET MVC 2 does the rest by resolving which Template Helper to use!

I modified the Master Page some to suite my needs (I use divs instead of tables), but overall it is the same one that he lays out on his blog.

EditorTemplates/Tempate.Master

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<script runat="server">
    protected override void OnInit(EventArgs e) {
        base.OnInit(e);
 
        if (ViewData.ModelMetadata.HideSurroundingHtml) {
            TablePlaceholder.Visible = false;
        }
        else {
            Controls.Remove(Data);
            DataPlaceholder.Controls.Add(Data);
        }
    }
</script>
<asp:ContentPlaceHolder runat="server" id="Data" />
<asp:PlaceHolder runat="server" id="TablePlaceholder">
    <div class="displayLayout">
        <div class="displayLabel">
            <asp:ContentPlaceHolder runat="server" id="Label">
                <%= ViewData.ModelMetadata.IsRequired ? "*" : "" %>
                <%= ViewData.ModelMetadata.GetDisplayName() %>
            </asp:ContentPlaceHolder>
        </div>
        <div class="displayField">
            <asp:PlaceHolder runat="server" id="DataPlaceholder" />
            <asp:ContentPlaceHolder runat="server" ID="Validation">
                <%= Html.ValidationMessage("") %>
            </asp:ContentPlaceHolder>            
        </div>
        <div style="clear: both;"></div>
    </div>    
</asp:PlaceHolder>

EditorTemplate/String.aspx

<%@ Page Language="C#" MasterPageFile="Template.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="Data" runat="server">
    <%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
                     new { @class = "text-box single-line" }) %>
</asp:Content>

There are several other files that you’ll need. I’ve grabbed most of them from Brad’s blog post and have them in the demo application below that you can download.

0003d725337bf3a4617919ac6126dc07PicNik

Now we can try the above scenario one more time and get the output that we were expecting.

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>

    <%= Html.EditorFor(m => m.FirstName) %>
    <%= Html.EditorFor(m => m.LastName)%>
    <%-- Purposely Not Show the Email –%>

    <p class="actions"> 
        <input type="submit" value="Create" /> 
    </p> 

<% } %>

 

Mvc2TemplatedHelperAfterPicNik

 

cooltext439925016

Friday, October 09, 2009

ASP.NET MVC Cheat Sheets

Most of you have seen many jQuery Cheat Sheets out in the wild, but as of recently there are now also many ASP.NET MVC Cheat Sheets to chose from.

If you know of any other ASP.NET MVC Cheat Sheets, please let me know and I’ll add it to the above list. I hope these prove to be helpful for you. You might consider printing them out and posting them on your cubical walls ;)

Note: Instead of hosting the PDF’s on my server, I am pointing to the source website for you to download the Cheat Sheets from there.

Getting Started with ASP.NET MVC 1.0


DZone

You'll learn how to setup your environment and how to create a web application. Then, you'll get to go deeper into detail and learn about components of the framework along with the structure of the main API.

ASP.NET MVC: The Request-Handling Pipeline


RequestHandling

Illustrates which parts of the ASP.NET MVC Framework get called when a request is made. Check out the purpose and features of routing, controllers, actions, and views.

ASP.NET MVC: View Cheat Sheet


ViewCheatSheet
The View Cheat Sheets focuses on the HTML Helpers, URL Helpers and so on that you would use within your views.

ASP.NET MVC: Controller Cheat Sheet


Controllers

The Controller Cheat Sheet focuses on what you return from your controller and how to use them and it also includes a lot of information on the MVC specific attributes.

ASP.NET MVC: Framework Cheat Sheet

Framework

Framework Cheat Sheet focuses on the rest of MVC like routing, folder structure, execution pipeline etc.

ASP.NET MVC: Proven Practices Cheat Sheet

ProvenPractices

Proven Practices Cheat Sheet contains ten KEY learnings that every ASP.NET MVC developer should know - it also includes links to the experts in this field where you can get a ton more information on those key learning's. 

ASP.NET MVC 101 Sheet

aspnetmvcCheatSheet

This sheet contains some snippets and information about ASP.NET MVC 1.0, César Intriago compiled most of this information from the ASP.NET MVC official site and from the free chapter of ASP.NET MVC 1.0 eBook:

Thursday, October 08, 2009

Using MvcContrib ScriptInclude, Stylesheet, And T4MVC

I am always looking for more ways I can integrate features of MVC Contrib into my ASP.NET MVC projects. I also have started using David Ebbo’s T4MVC Template that generates strongly typed helpers for ASP.NET MVC (download).

Before I integrated these tools my script and style includes looked like…

<script src="../../Content/Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="../../Content/Scripts/jquery-ui-1.7.2.custom.min.js" type="text/javascript"></script>

<link href="../../Content/Styles/Site.css" rel="stylesheet" type="text/css" />
<link href="../../Content/Styles/start/jquery-ui-1.7.2.custom.css" rel="stylesheet" type="text/css" />

I remembered hearing about ScriptInclude and StyleInclude Html Helpers in the MVC Contrib so I updated the above references to the following…

<%= Html.ScriptInclude("~/Content/Scripts/jquery-1.3.2.min.js")%>
<%= Html.ScriptInclude("~/Content/Scripts/jquery-ui-1.7.2.custom.min.js")%>
    
<%= Html.Stylesheet("~/Content/Styles/Site.css")%>
<%= Html.Stylesheet("~/Content/Styles/start/jquery-ui-1.7.2.custom.css")%>

I was please about using the MVC Contrib Helpers, but I wasn’t thrilled with having hard-coded strings laying around which is where the T4MVC Template comes into play.

There are many features of the T4MVC Template (many more than I planned on covering today), but one of them is interrogating your project structure and generating static classes with read-only references to your Scripts, Styles, and Images.

So, after running the T4MVC Template, I updated my references to the following…

<%= Html.ScriptInclude(Links.Content.Scripts.jquery_1_3_2_min_js)%>
<%= Html.ScriptInclude(Links.Content.Scripts.jquery_ui_1_7_2_custom_min_js)%>
    
<%= Html.Stylesheet(Links.Content.Styles.Site_css)%>
<%= Html.Stylesheet(Links.Content.Styles.start.jquery_ui_1_7_2_custom_css)%>

Looks pretty good, doesn't it? Well, there is only one problem… it doesn’t work! Why? Well, the output of the T4MVC Links are relative paths that have been resolved (meaning they no longer have the “~”). The MVC Contrib Helpers assume that if the URL passed it it doesn’t have the “~”, then it will prepend either “~/Scripts/” for scripts or “~/content/css/'” for styles.

Seeing that I have moved my scripts, styles, and images under the “~/Content” folder, there are a couple of changes to the MVC Contrib Html Helpers that could make this work…

    1. Provide some sort of mechanism to define the paths prepended to the Scripts and Styles if there is no “~”
    2. Override the Html Helpers with another option to not prepend any path information
    3. Possibly search for the “/” instead of the “~” when determining if a path should be prepended to the URL
    Can you think of any other solutions to get these to play well together?

Sunday, September 20, 2009

Using StructureMap with ASP.NET MVC & MVC Contrib

I’ve found myself using the MVC Contrib project more and more lately. There are tons of golden framework nuggets just waiting to be used.

I recently integrated StructureMap into the my current ASP.NET MVC framework using MVC Contrib. The reason I decided to write this post is because most of the resources I found on the internet appeared to be a little out of date and used deprecated StructureMap APIs. So, here it goes…

Note: The following examples are using StructureMap v2.5.3 & ASP.NET MVC v2 Preview 1 & MVC Contrib MVC2 Branch code bits. You may find minor differences in API &| syntax if you are using a different version.

First lets investigate why StructureMap is necessary in the first place. You can find some good blog posts by Jeremy D. Miller about the basics of the Dependency Injection Pattern and using an IOC tool.

In order to facilitate mocking and decouple our application we pass an interface of our service into our controller instead of a concrete class. The default controller factory that ASP.NET MVC uses requires a default constructor to be present, but we are going to define our own Controller Factory later in this post using one of the MVC Contrib classes.

Note: There is actually a really good screencast with @robconery and @jeremydmiller about using StructureMap in ASP.NET MVC. There were several “Aha!” moments for me as I watched it. The StructureMap API has changed slightly since the screencast, but I will show the updated syntax in the following of this post.

The following is a typical ContactController class that will house the Index, Details, Create, Edit, and Delete actions. You will notice that instead of having a default constructor, I have an overloaded contructor and am passing in an interface to my service. I will wire up StructureMap to handle passing in the appropriate object later in this post.

public partial class ContactController : Controller
{
    private IContactService service;

    public ContactController(IContactService service)
    {
        this.service = service;
    }
}    

The wiring part, happens typically in the Application_Start event from the Global.asax.cs file. In addition to Registering your MVC routes (which should have already been wired up when you created your MVC application) you need to both configure StuctureMap to know what concrete classes map to what interfaces as well as tell MVC to use StructureMap to create its controllers.

public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        RegisterRoutes(RouteTable.Routes);

        Bootstrapper.ConfigureStructureMap();
        ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
    }
}

I am going to attempt and explain the above code snippet line by line, so lets start with the Bootstrapper.ConfigureStructureMap() and then we will discuss the StructureMapControllerFactory().

After everything is said and done, the important part of StructureMap is that it knows what interfaces should map to what concrete types so that it can inject the appropriate instances at runtime. This is where the Bootstrapper.ConfigureStructureMap() comes into play.

public static class Bootstrapper 
{
    public static void ConfigureStructureMap()
    {
        ObjectFactory.Initialize(x => x.AddRegistry(new MyApplicationRegistry()));            
    }
}

public class MyApplicationRegistry : Registry
{
    public MyApplicationRegistry()
    {
        Scan(assemblyScanner =>
        {
            assemblyScanner.TheCallingAssembly();
            assemblyScanner.WithDefaultConventions();
        });
    }
}

The above code is initializing StructureMap with the MyApplicationRegistry that contains the rules for the interface & concrete type mappings. You may be wondering, “But I don’t see where IContactService is mapped to ContactService” and that is a very good question. The answer is that StuctureMap takes the Convention Over Configuration approach and tries to take some educational guesses based on a set of default naming conventions.

Lets say that your configuration isn’t following standard naming conventions. Can you still use StructureMap? Well, of course you can :) You have full control over the mappings and can set them up however you wish. The following is an example of me manually doing the mapping instead of using the default naming conventions. The Bootstrapper remains the same, so I only will show the code that is different below…

public class MyApplicationRegistry : Registry
{
    public MyApplicationRegistry()
    {
        ForRequestedType<IContactService>().TheDefaultIsConcreteType<ContactService>();
        ForRequestedType<IValidationRunner>().TheDefaultIsConcreteType<ValidationRunner>();
    }
}

Now lets focus on the StructureMapControllerFactory that we saw after we Configured StructureMap from the Global.asax. The StructureMapControllerFactory class that I am instantiating actually comes with the MVC Contrib project.  The contents of this class isn’t really all that complicated, but its one less thing you have to write by hand. The following is an example of a oversimplified implementation of the StructureMapControllerFactory that you could write yourself…

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return ObjectFactory.GetInstance(controllerType) as IController;
    }
}

Since we separated our dependencies and used StructureMap for injection our application is now loosely coupled and our ability to Unit Test more areas has increased.

Stay tuned for a new series where I will upgrade a standard ASP.NET MVC project to ASP.NET MVC 2 and then integrate StructureMap, Moq, MbUnit, and suite of Unit Tests.

Thursday, September 10, 2009

ASP.NET MVC 1.0 TDD Book Review

AspNetMvcTdd I recently finished reading Emad Ibrahim’s ASP.NET MVC 1.0 Test Driven Development book and I thought I would do a detailed review and share my thoughts and findings.
Before I get into the details of the book, it is important to know that the audience of this book is not for novice programmers or experience non ASP.NET developers. To really get value from this book, you should have some decent exposure to ASP.NET under your belt.
With that said, this book is a very good overview of both ASP.NET MVC and Test Driven Development (TDD). It takes the reader through the process of developing a whole application using the Test First methodology. I found this interesting in that some decisions Emad made initially were later refactored in the book as the application evolved and changed, which is a great way to see the value of TDD and its approach.
This book covers quite a few advanced programming concepts that may be new to you such as various Design Patterns (Strategy, Null Objects, Repository, etc…), Design Principles (Open-Closed Principle, YAGNI, DRY, Inversion of Control, Single Responsibility, Convention Over Configuration, etc…). Emad does a good job about explaining these concepts as he approaches them in the application.
About the same time that I was reading this book, I was in the process of evaluating various tools and frameworks to use for a new ASP.NET MVC v2 project I am working on. I found it interesting that Emad ended up choosing most of the same tools that I had at the time such as: MbUnit, Ninject, Moq, and jQuery. If you are familiar with tools other than the ones he chose at the time of writing this book, you should be able to easily substitute your favorite tool instead. Most of the competitors for these tools have basically the same features.
Since this is a new book, I did run across numerous typos, references to code that wasn’t displayed, refactored code that wasn’t indicated in the text, and several other minor issues, but all in all I knew what Emad was getting at and it wasn’t hard to follow his train of thought. I submitted these inconsistencies to the Errata to hopefully clean up these small issues for future printings of this book. I posted my findings and you can review them online.
I highly recommend downloading the source code from this book (which you can do for free). It is one of the few projects that I’ve seen that has a plethora of Unit Tests to look at and get an idea of how to test your ASP.NET MVC project. I’ve seen numerous other ASP.NET MVC projects that only have a minimal number of Unit Tests and don’t really give you a good idea of how you could get good Code Coverage.
If you are interested in getting a jumpstart into both ASP.NET MVC and Test Driven Development, then I think this is a great book for you to get. However, if you aren’t so sure about TDD and you just want to get up to speed on ASP.NET MVC, then I might recommend you get one of the other beginning ASP.NET MVC books such as:
    Note: A danger of listing books is that I may have missed one ;) If so, please leave a comment listing any of another ASP.NET MVC book published as of the date of this blog post and I will be happy to add it to the list.
I haven’t read any of the above books yet, but I do hope to in the near future and as I do I plan to perform book reviews for those as well.
Thank you Emad for all of your hard work on your book. I enjoyed reading it over my extended Labor Day weekend :)
Updated: You can view the Errata document I put together listing all the inconsistencies, typos, minor issues, etc… online.

Friday, August 28, 2009

Getting Started with Telerik Extensions for ASP.NET MVC

I initially decided to try out the Telerik Extensions for ASP.NET MVC because I was looking to create a EditorTemplate (ASP.NET MVC v2 Preview 1 feature) for Date and I wanted to use the jQuery UI DatePicker wrapper that Telerik provides.

Not only was I pleased with the Fluent Interface that they used to wrap the jQuery UI Plugins, but I was also surprised and impressed by their Script and Style management system.

Web.config Changes

The first thing you’ll need to do is to make a couple of web.config changes to the Handler sections before starting to use the Telerik Extensions.

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc"/>
        </httpHandlers>
    </system.web>
    <system.webServer>
        <handlers>
            <remove name="AssetHandler"/>
            <add name="AssetHandler" preCondition="integratedMode" verb="GET,HEAD" path="asset.axd" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc"/>      
        </handlers>
    </system.webServer>    
</configuration>

Including jQuery & jQuery UI

Before you can start using the jQuery UI wrappers, you’ll need to first include jQuery & jQuery UI into your project. For my simple project, I just did the following…

Site.Master MasterPage

<%@ Import Namespace="Telerik.Web.Mvc.UI"%>
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <% Html.Telerik().StyleSheetRegistrar()
        .DefaultGroup(group => group
            .Add("Site.css")
            .Add("jquery-ui-1.7.2.custom.css").Render(); %>              
</head>        
<body>
    <!-- Misc -->
    <%  Html.Telerik().ScriptRegistrar().Render(); %>            
</body>
</html>

The code will automatically insert jQuery by default and if you happened to use one of the Telerik jQuery UI Extensions then it will insert jQuery UI as well.

Note: It is important to know that the ScriptRegistrar must be directly before the end body tag in order to work.

Date EditorTemplate

Before creating the EditorTemplate, I annotated the DateOfBirth property with DataType.Date so the Template framework has additional metadata to work with.

Employee.cs View Model

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using NestedTemplates.Helpers;

namespace TelerikExtensions.Models
{
    public class Employee
    {
        [UIHint("Hidden")]
        public long Id { get; set; }

        [Required(ErrorMessage = "First Name Required")]
        [StringLength(50, ErrorMessage = "Must be less than 50 characters")]
        [DisplayName("First Name")]
        public string FirstName { get; set; }

        [Required(ErrorMessage = "Last Name Required")]
        [StringLength(50, ErrorMessage = "Must be less than 50 characters")]
        [DisplayName("Last Name")]
        public string LastName { get; set; }

        [Required(ErrorMessage = "Social Security Required")]
        [StringLength(50, ErrorMessage = "Must be less than 50 characters")]
        [RegularExpression(@"^[0-9]{3}-?[0-9]{2}-?[0-9]{4}$", ErrorMessage = "Invalid Social Security Number Format")]
        [DisplayName("Social Security Number")]
        public string SocialSecurity { get; set; }

        [Required(ErrorMessage = "Date of Birth Required")]
        [StringLength(50, ErrorMessage = "Must be less than 50 characters")]
        [DataType(DataType.Date, ErrorMessage = "Date of Birth must be expressed as a date")]
        [DisplayName("Date of Birth")]
        public DateTime DateOfBirth { get; set; }
    }
}

Then I went ahead and made a EditorTemplate called Date.ascx (which matches the DataType.Date name) using the Telerik DatePicker Extension

Date.ascx EditorTemplate

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<%@ Import Namespace="Mvc.UI.jQuery"%>

<% Html.jQuery().DatePicker()
    .Name("dateField")
    .Value((DateTime) Model).Render(); %>

Since we annotated the DateOfBirth property in our View Model with DataType.Date there isn't anything really special you need to do for ASP.NET MVC v2 Preview 1 to use it other than calling Html.EditorFor(m => m.DateOfBirth). Behind the scenes it matches up the types and uses the above EditorTemplate.

Edit.aspx View

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TelerikExtensions.Models.Employee>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Edit</h2>
    
    <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
    
    <% using (Html.BeginForm()) {%>
        <p>
            <%= Html.LabelFor(m => m.FirstName) %>
            <%= Html.EditorFor(m => m.FirstName) %>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <%= Html.LabelFor(m => m.LastName) %>
            <%= Html.EditorFor(m => m.LastName) %>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
        <p>
            <%= Html.LabelFor(m => m.SocialSecurity) %>
            <%= Html.EditorFor(m => m.SocialSecurity) %>
            <%= Html.ValidationMessage("SocialSecurity", "*") %>
        </p>
        <p>
            <%= Html.LabelFor(m => m.DateOfBirth) %>
            <%= Html.EditorFor(m => m.DateOfBirth) %>
            <%= Html.ValidationMessage("DateOfBirth", "*") %>
        </p>   
        <p>
            <input type="submit" value="Save" />
        </p>
    <% } %>
    
    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>
</asp:Content>

jQuery UI Goodness

The result is what you would expect if you had manually wired up the jQuery UI DatePicker into your project, but this time with strong type checking and a nice Fluent Interface.

jQueryUiDatePickerGoodness

Compress, Combine, & Cache

Now, what I thought was even cooler than that was that I could specify to combine, compress, and cache the scripts & styles… how cool is that!?!

<%@ Import Namespace="Telerik.Web.Mvc.UI"%>
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <% Html.Telerik().StyleSheetRegistrar()
        .DefaultGroup(group => group
            .Add("Site.css")
            .Add("jquery-ui-1.7.2.custom.css")
            .Combined(true)
            .Compress(true)).Render(); %>              
</head>        
<body>
    <!-- Misc -->
    <%  Html.Telerik().ScriptRegistrar().
            DefaultGroup(group => group
                .Combined(true)
                .Compress(true)
                .CacheDurationInDays(365)).Render(); %>            
</body>
</html>

Instead of compressing, combining, & caching everything together you could also choose to separate like scripts in groups like the following…

<% Html.Telerik().ScriptRegistrar().Scripts(script => script
        .AddGroup("Validation", group => group
            .Add("~/Content/Scripts/jquery.validate.min.js")
            .Add("~/Content/Scripts/xVal.jquery.validate.js")
            .Combined(true)
            .Compress(true)
            .CacheDurationInDays(365))
        .AddGroup("TableSorter", group => group
            .Add("~/Content/Scripts/metadata.js")
            .Add("~/Content/Scripts/jquery.tablesorter.js")
            .Add("~/Content/Scripts/jquery.tablesorter.pager.js")
            .Combined(true)
            .Compress(true)
            .CacheDurationInDays(365))                
    ).Render(); %>   

Gotchas

The next step was to integrate the test code into my main project. I had some initial issues resolving the jQuery & jQuery UI scripts. Initially, the Telerik Extensions look in the standard Scripts folder that the ASP.NET MVC project creates. However, I moved my Scripts folder under the Content folder alongside the Styles folder. Thanks to Todd Anglin, Kazi Manzur Rashid, & some research into their codebase I was able to put the following two lines of code in my Global.asax to override the default folder for the scripts.

using System.Web.Mvc;
using System.Web.Routing;
using Telerik.Web.Mvc;
using Telerik.Web.Mvc.UI;

namespace TelerikExtensions
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);

            ScriptRegistrar.FrameworkScriptPath = "~/Content/Scripts";
            WebAssetDefaultSettings.ScriptFilesPath = "~/Content/Scripts";
        }
    }
}

Summary

All in all, this is a very cool open source library that I think would be beneficial for most any ASP.NET MVC application.

Feel free to download the sample project I put together using most of the above code.

Friday, August 21, 2009

ASP.NET MVC V2 Preview 1 Strongly Typed UI Helper Performance

I am sure many of you ASP.NET MVC enthusiasts have either examined, downloaded, or started to play around with the ASP.NET MVC V2 Preview 1 release.

One of the many new features is the concept of Strongly Typed UI Helpers such as Html.DisplayFor() & Html.EditorFor() which take a Lambda Expression as their argument. This is a great new feature because we can now get compile time checking of our arguments and it provides the ability to easily refactor changes across our project. The old version of the Html Helpers were littered with Magic Strings that were susceptible to error and painfully resistant to change.

I was talking to a co-worker at Sommet Group, Alex Robson, about the new Strongly Typed UI Helpers and he immediately became curious as to whether the helpers use Lambda Compiles to obtain the Model’s value. I checked the source code of the ASP.NET MVC V2 Preview 1 release and found the following code snippet from TemplateHelpers.cs…

internal static void TemplateFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldId, DataBoundControlMode mode) where TModel : class {
   object modelValue = null;
   try {
      modelValue = expression.Compile()(html.ViewData.Model);
   }
   catch (NullReferenceException) { }  // Okay if it throws a null ref exception, we can infer types without the actual data
   //Etc...
}

The reason for the concern is that he read a recent slide deck by Rudi Benkovic reviewing several performance issues of which Lambda Compiles were a hot topic.

The Experiment

With Alex’s experience developing the Open Source project Nvigorate, he has had a lot of experience working with Lamdas, Reflection, and the like. So, he started on a mission to compare various dynamic access methods and to compare their performance. The following graph provides a high level summary of Alex's findings. You can also read his detailed post from his blog and download his sample code. 

DynamicAccessMethodPerformance

As you can see from the graph above, the Lambda Compile is by far the least efficient way to obtain the model’s value when compared with Dynamic Methods, Type Descriptor, and Reflection. The worst part is that the Lambda Compile and Dynamic Method approaches both scaled poorly when the number of test cases increased.

Why Should I Care?

You might be thinking, “Who would actually use 1,000 or even 10,000 instances of the Html.EditorFor() anyway? Why does this really even matter?”.

The answer is that we shouldn’t just be concerned about the current page request, but also with the overall scalability of our website as the number of concurrent page requests increase.

What Now?

The good news is that the API for the Strongly Typed UI Helpers don’t need to change in order to accommodate these speed performances. All of Alex’s tests start with a Lambda Expression…

public static void Reflect<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, TModel instance)
{
    var val = typeof(TModel).GetProperty(GetMemberFromExpression(expression)).GetValue(instance, new object[] {});
    Debug.Assert(val.Equals("Dude"));
}

public static void TypeDescriptorCall<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, TModel instance)
{
    var properties = TypeDescriptor.GetProperties(typeof(TModel));
    var property = properties.Find(GetMemberFromExpression(expression), false);
    TProperty val = (TProperty)property.GetValue(instance);
    Debug.Assert(val.Equals("Dude"));
}

public static void DynamicMethodCall<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, TModel instance)
{
    var delegateCall = DynamicMethodFactory.CreateGetter(typeof(TModel).GetProperty(GetMemberFromExpression(expression)));
    var val = delegateCall(instance);
    Debug.Assert(val.Equals("Dude"));
}

public static void LambdaCompile<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, TModel instance)
{
    var val = expression.Compile()(instance);
    Debug.Assert(val.Equals("Dude"));
}

Summary

Based on some initial work done by Rudi Benkovic as seen in his slide deck, it came to our attention that Lamda Compiles can be very slow and actually affect website scaling when using ASP.NET MVC.

Thanks to Alex Robson, he compared 4 different dynamic access methods and compared their performance. His findings showed that using a Lamda Compile was the slowest of the 4 approaches tested when obtaining a Model’s value from a Lambda Expression. In contrast, Reflection was by far the fastest approach to consistently retrieve a Model’s value.

I hope the ASP.NET MVC team will take note of these findings and consider using Reflection instead of Compiling the Lamda inside of their next preview release.

Wednesday, July 08, 2009

ASP.NET MVC & jQuery Part 3: MvcContrib Grid & jQuery Plugins

This is the 3rd blog post in a series taken from a recent ASP.NET MVC & jQuery presentation I gave at CodeStock. The previous blog posts are available here...

  1. Part 1: Adding jQuery Intellisense to VS 2008
  2. Part 2: Zebra Striping

MVC Contrib Grid Component

The ASP.NET MVC 1.0 Release has a lot of great things included in it, but there is another open source project called MVC Contrib that fills many gaps and compliments the core functionality.

In this blog post I want to demonstrate how you can use the MVC Contrib Grid Component in conjunction with two different jQuery plugins to provide rich functionality inside a simple and clean View.

First lets review why you might want to use the MVC Contrib Grid Component.

  • Provides a Fluent Interface
  • Discourages Logic in your View
  • Provides Reusable Grid Models
  • Customizes Grid Rendering
  • Includes a Separate Pagination Component

If the above bullet items interest you, then check out the 5 part blog series on the MVC Contrib Grid Html Helper by Jeremy Skinner.

Enough of describing the component, lets start using the MVC Contrib Grid. After adding a reference to the MvcContrib assembly to your project you can proceed adding the Grid to your View…

<% Html.Grid(Model).Columns( column => {   
   column.For(x => Html.ActionLink("ID", "Details", new { id = x.Id })).Named("View").DoNotEncode();   
   column.For(x => x.Name).Named("Pet Name");   
   column.For(x => x.Description).Named("Description");   
   column.For(x => x.Price).Format("{0:C}"); 
})
.Attributes(style => "width: 75%;")
.Empty("There are no pets.")
.Render(); %>

As you can see in the code above, the syntax is easy to ready and it utilizes the tightly bound model passed to the View. The output of the Grid isn’t anything spectacular (see the following grid).

  <table id="dataTable">
        <thead>
            <tr>
                <th>View</th>
                <th>Pet Name</th>
                <th>Description</th>
                <th>Price</th>
            </tr>
        </thead>
        <tr class="gridrow">
            <td><a href="/MvcContrib/Details/1">ID</a></td>
            <td>Ralph Fits</td>
            <td>A lonely ragged pup that licks himself and isn't house trained</td>
            <td>$12.99</td>
        </tr>
        <tr class="gridrow_alternate">
            <td><a href="/MvcContrib/Details/2">ID</a></td>
            <td>Fluffy Florentine</td>
            <td>A sweet little kitten that is ready to have a home</td>
            <td>$277.00</td>
        </tr>
        <!-- Additional Rows -->
        <tr class="gridrow_alternate">
            <td><a href="/MvcContrib/Details/14">ID</a></td>
            <td>Alien Abbott</td>
            <td>You never hear this dog bark because it only barks in the future.</td>
            <td>$777.99</td>
        </tr>
        <tr class="gridrow">
            <td><a href="/MvcContrib/Details/15">ID</a></td>
            <td>Crazy Calico</td>
            <td>If you like cats, don't get this one because it's pure crazy on a stick!</td>
            <td>$2.99</td>
        </tr>
    </tab

We can now transform the output of the MVC Contrib Grid into a rich featured table with the help of a little jQuery.

jQuery TableSorter Plugin

First, lets use the jQuery TableSorter Plugin to add some simple inline sorting capabilities to the Grid. Only the following one line of jQuery is necessary to transform this Grid. See the follow table for the results.

Note: TableSorter has additional widgets you can add for extra functionality. I am using the zebra widget to provide the alternate row coloring.

$("table").addClass('tableSorter').tablesorter({ sortList: [[1, 0]], widgets: ['zebra'] });  

ViewPet NameDescriptionPrice
IDRalph FitsA lonely ragged pup that licks himself and isn't house trained$12.99
IDFluffy FlorentineA sweet little kitten that is ready to have a home$277.00
IDBubba GumpA very stupid dog that can't even find his way to his dog house$999.99
IDCharlie StrangoThis weird looking puppy scares most adults, but has a strange interest to children$23.44
IDBlacky the BeautifulThis beautiful cat is as kind as can be. Who says that a dog is man's best friend?$45.65
IDSnot Nose NellyIf you want to get wet, then this is the pet for you. Talk about snot everywhere!$44.78
IDDarcy ScoopA wonderful black lab with human-like intelligence; however, it does poop a lot.$234.55
IDSupsrirooantsumA very rare cat straight from it's homeland of India.$432.67
IDClark KentA self-employed kitten making and selling fur balls for all to enjoy.$44.66
IDRed Headed BullThis dog is unlike any other that you have seen. You can even braid it's flowing red hair.$78.99
IDRex the TerribleA half german shepard / half wolf aggresive dog that will tear the flesh out of anyone you wish.$346.99
IDSkittish SteveYou will never see this dog once you buy it. It'll be like you never had a pet to begin with.$33.00
IDDart MouthA dart gun was surgically grafted into this dog's mouth. He is great for parties.$55.00
IDAlien AbbottYou never hear this dog bark because it only barks in the future.$777.99
IDCrazy CalicoIf you like cats, don't get this one because it's pure crazy on a stick!$2.99

jQuery DataTable Plugin

Now, lets try adding a s lightly more feature rich jQuery Plugin to our Grid. The jQuery DataTable Plugin allows not only sorting of columns, but provides an elegant searching mechanism and also paging functionality. Yet again, with only one jQuery statement we can transform this table into something much more usable.

$('dataTable').addClass('dataTable').dataTable({
   "iDisplayLength": 10,
   "aaSorting": [[1, "asc"]],
   "aoColumns": [{ "bSortable": false }, null, null, null]
}); 

ViewPet NameDescriptionPrice
IDRalph FitsA lonely ragged pup that licks himself and isn't house trained$12.99
IDFluffy FlorentineA sweet little kitten that is ready to have a home$277.00
IDBubba GumpA very stupid dog that can't even find his way to his dog house$999.99
IDCharlie StrangoThis weird looking puppy scares most adults, but has a strange interest to children$23.44
IDBlacky the BeautifulThis beautiful cat is as kind as can be. Who says that a dog is man's best friend?$45.65
IDSnot Nose NellyIf you want to get wet, then this is the pet for you. Talk about snot everywhere!$44.78
IDDarcy ScoopA wonderful black lab with human-like intelligence; however, it does poop a lot.$234.55
IDSupsrirooantsumA very rare cat straight from it's homeland of India.$432.67
IDClark KentA self-employed kitten making and selling fur balls for all to enjoy.$44.66
IDRed Headed BullThis dog is unlike any other that you have seen. You can even braid it's flowing red hair.$78.99
IDRex the TerribleA half german shepard / half wolf aggresive dog that will tear the flesh out of anyone you wish.$346.99
IDSkittish SteveYou will never see this dog once you buy it. It'll be like you never had a pet to begin with.$33.00
IDDart MouthA dart gun was surgically grafted into this dog's mouth. He is great for parties.$55.00
IDAlien AbbottYou never hear this dog bark because it only barks in the future.$777.99
IDCrazy CalicoIf you like cats, don't get this one because it's pure crazy on a stick!$2.99



Now, there are definitely some pros and cons in using the above two jQuery plugins.

Pros

  • The TableSorter Plugin & DataTable Plugin are very easy to implement. Only one jQuery statement is needed.
  • The user is presented with a richer interface providing sorting, paging, and simple searching.

Cons

  • Despite the pros listed above, the biggest con is that the jQuery Plugins are not scalable. All the records are returned in the View and the plugins hide and show the necessary data depending upon the user’s actions.

In light of the cons, I personally wouldn’t use any of the above jQuery plugins for my tables unless it was for a trivial non-production web page with a known number of static records.

Normally, I lead towards using either the Flexigrid Plugin or jqGrid Plugin that I will be reviewing in the next blog post in this series.