Showing posts with label MVC Contrib. Show all posts
Showing posts with label MVC Contrib. Show all posts

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

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.