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

Wednesday, April 28, 2010

Tips using Firebug Lite and Full Screen with jsFiddle

jsFiddle

I’ve been using jsFiddle more and more to test and share JavaScript and jQuery code.

Although I love that jsFiddle shows the HTML, JavaScript, CSS, and Results window all at the same time, I also would love to somehow see the output of the console window in the User Interface. Depending on the browser someone views my jsFiddle projects, the console may or may not be defined. I really don’t like alert statements and sometimes it is just easier to console.log() than it is to write out messages to the DOM.

Firebug Lite

So, I’ve been finding myself adding Firebug Lite as a resource to jsFiddle and letting Firebug be part of the rendered output.

If you’ve tried this, then you may have noticed that Firebug auto-opens once jsFiddle is rendered. Seeing that the Rendered section isn’t very big this could be a big problem.

jsFiddleFireBugBig

Thankfully, there are several options you can provide to Firebug Lite to customize it’s behavior.

I find myself setting the following three options on a regular basis.

<script type="text/javascript">
    firebug.env.height = 200; //Set the initial height of the Firebug window
    firebug.env.debug= false; //Set Firebug to be initially launched or minimized
    firebug.env.detectFirebug = true; //If browser has Firebug, don’t interfere with it
</script>

 

jsFiddleFireBugMinimized

I also like to use JsBin quite a bit and honestly switch between it and jsFiddle quite often depending on what I am doing.

Full Screen Option

One of the features that I really like in JsBin is that you can launch the session to Full Screen and not inside the editor. This is helpful if you want to interact with the DOM with your browser’s debugging tools, minimize any side-effects the editor might be creating, etc…

As it turns out, jsFiddle has this same feature, but you have to know how to get to it. You can just add “/show” to the end of your jsFiddle URL to show it without the editor. To swap back to the edit view you can simply remove the “/show” from the URL.

Full Screen / Editor Toggle Button

Now that we know we can swap back and forth between Full Screen and Edit mode, what I miss is the nice little JsBin link in the upper-right hand corner of the page allowing you to easily swap between the two environments.

JsBin

As it turns out, I’ve recreated this in jsFiddle by adding the following code to my JavaScript window…

$("<a />", {
    id : "show",
    text : window.location.pathname.indexOf("show/light") > 0 ?
    "View Full Screen" : "Edit using jsFiddle",
    href : window.location.pathname.indexOf("show/light") > 0 ?
        window.location.pathname.replace(/light\/$/gi, "") :
        window.location.pathname.replace(/show\/$/gi, ""),
    target : "_blank"
}).appendTo('body');  

…and here is the supporting CSS that I put in the CSS window of the jsFiddle Editor.

#show {
    position: fixed;
    top: 0px;
    right: 0px;
    padding: 5px;
    background-color: #3D6F9A;
    color: #ffffff;
    border-bottom: 1px solid rgb(153, 153, 153);    
    border-left-width: 1px solid rgb(153, 153, 153);
    -webkit-border-bottom-left-radius: 10px;
    -moz-border-radius-bottomleft: 10px;
    border-bottom-left-radius: 10px;     
    border-right-width: 0px;
    border-top-width: 0px;
    text-decoration: none;
    color:#FFFFFF;
    font-size:11px;
    font-weight:bold;
    text-decoration:none;
    text-shadow:0 1px 0 #0C131C;
    font-family:"Lucida Grande","Lucida Sans","Lucida Sans Unicode","Luxi Sans",Tahoma,sans-serif;
    display: block;
}

The following screenshot is what the result window looks like inside the jsFiddle Editor

jsFiddleFullScreen

The following screenshot is what the window looks like in jsFiddle Full Screen mode

 jsFiddleFullScreen2

You can view, run, and edit the above code snippets using my jsFiddle

cooltext439925164

Tuesday, April 27, 2010

Stepping Down from The Official jQuery Podcast

ps.nrdkgcvf.170x170-75 In case you haven’t heard from the end of the Rey Bomb #2 Episode of The Official jQuery Podcast, I announced that after 21 episodes I am stepping down as my role of co-host.

Around the time I first joined the podcast I also started to become involved with some intense writing projects (as will become apparent in the near future), although I can’t say much about it right now :)

As a result, I haven’t had much margin in my life. I’ve been running on empty for quite some time and other more important things in my life have gotten the shaft! Unfortunately, one of those was my family and I intend to change that.

I don’t plan to fall off the face of the earth. I plan to refocus on blogging and tweeting. I would like to do some more speaking, but probably not for the rest of the year as baby #3 is coming in August.

I am honored to have been asked to join The Official jQuery Podcast and during my involvement I got to meet many great people that I hadn’t previously known. I am glad to have formed these relationships, and look forward to further investing in them.

I hope the future of The Official jQuery Podcast is bright and I encourage you to either start or continue to listen.