OdeToCode IC Logo

Drop-down Lists and ASP.NET MVC

Monday, January 18, 2010 by K. Scott Allen

Working with drop-down lists in ASP.NET MVC has some confusing aspects, so let’s look at an example.

Imagine the goal is to edit a song (not the music and lyrics of a song – just the boring data pieces). Each song is associated with an album, and each song has a title and track number. With this description, you can imagine an edit view using the following code:

<%= Html.DropDownList("AlbumId", Model.Albums)%>
  ...
<%= Html.TextBox("Title", Model.Title) %>
   ...
<%= Html.TextBox("TrackNumber", Model.TrackNumber) %>

The Html.DropDownList helper method likes to work with SelectListItem objects, so a view model you can pair with this view looks like the following:

public class EditSongViewModel
{        
    public string Title { get; set; }                
    public int TrackNumber { get; set; }
    public IEnumerable<SelectListItem> Albums { get; set; }
}

Some people don’t like to use SelectListItem types in their view models. Instead, they’ll convert to them in the view. I think it’s entirely reasonable to use SelectListItem in a view model, because the view model is supposed to make the view easier to write. Having the view model perfectly aligned with the needs of the view means you need to think less (and write less code) when creating the view.

Creating SelectListItems

There are a couple approaches you can take when creating a sequence of SelectListItem objects. I think the cleanest approach is to have an extension method that knows how to take a collection of objects in your software (like Album objects), and map them into SelectListItem objects.

public static IEnumerable<SelectListItem> ToSelectListItems(
              this IEnumerable<Album> albums, int selectedId)
{
    return 
        albums.OrderBy(album => album.Name)
              .Select(album => 
                  new SelectListItem
                  {
                    Selected = (album.ID == selectedId),
                    Text = album.Name,
                    Value = album.ID.ToString()
                   });
}

You can use the method like this:

model.Albums = _repository.FindAllAlbums().ToSelectItems(selectedId);

That code works, because Html.DropDownList will happily work with IEnumerable of SelectListItem.

The class you need to be careful with is the SelectList class. I’ve seen quite a few people make the mistake of wrapping their SelectListItem objects in a SelectList without setting the DataTextField and DataValueField properties. This does not work:

model.Albums = new SelectList(
                _repository.FindAllAlbums().ToSelectListItems(1)
                );

You’d think the SelectList class would know how to work with a collection of SelectListItem objects - but it doesn’t. The following doesn’t work either (the drop-down list will display “System.Web.Mvc.SelectListItem” for every entry):

model.Albums = 
    new SelectList(_repository.FindAllAlbums()
                              .ToSelectItems(selectedID));

The SelectList class is really designed to perform the conversion we did earlier (with the extension method), but it uses late binding reflection. The following would work, because we tell the SelectList where to find the text and value fields:

model.Albums = new SelectList(
                _repository.FindAllAlbums(), "ID", "Name"
                );

Reading the Selection

If you are using the same model to accept input from the edit view during a postback, you might think the default model binder will repopulate the Albums collection with all the album information and set the selected album. Unfortunately - the web doesn’t work this way and the Albums collection will be empty.

The only album related information the browser will post is the value of the selected item. If you want this value bound to a model, you’ll need to provide an AlbumId property (to match the name we gave the DropDownList in the view - “AlbumId”).

public class EditSongViewModel
{
    public int AlbumId { get; set; }
    public string Title { get; set; }                
    public int TrackNumber { get; set; }
    public IEnumerable<SelectListItem> Albums { get; set; }
}

Some people will create two separate view models in this case. One view model is designed to carry information to the view, and will have an Albums property (but no AlbumId property). The second view model is designed to accept user input during postback and will have an AlbumId propery (but no Albums property). This approach adds the overhead of an extra class, but the view models are perfectly aligned with their duties and no properties go unused. You’ll have to decide which approach is best for you.

Summary

  • Don’t use a SelectList without telling it the DataTextField and DataValueField properties to use.
  • Don’t expect to see a collection for a drop-down list repopulated on a postback.
  • Extension methods make it easy to create a sequence of SelectListItem objects in strongly-typed code.
  • Html.DropDownList doesn’t require a SelectList – it’s happy working with any sequence of SelectListItem objects.

T4MVC in MSDN Magazine

Thursday, January 14, 2010 by K. Scott Allen

msdn january 2010 The January 2010 issue of MSDN Magazine is online with my article covering T4MVC:

Microsoft Visual Studio includes a code generation engine known as T4 (which is short for Text Template Transformation Toolkit). You’ve probably already used T4 templates in Visual Studio without even knowing they were working behind the scenes. In this article I’m going to give you a basic introduction to T4 templates and show you how ASP.NET MVC uses this technology. I’ll also show you how to customize T4 templates to enhance your day-to-day work with the MVC framework

Thanks go to David Ebbo for his help and comments on the article. David continues to improve  T4MVC, for example: T4MVC 2.6.10: fluent route value API, shorter way to refer to action, and more

Enjoy!

Of Web Browsers and Humanity

Wednesday, January 13, 2010 by K. Scott Allen

Douglas Crockford posted an interesting topic for discussion on his site (look for the Discussion Topic section at the bottom of the page):

If a web browser is defective, causing errors in the display or performance of the page, should the page developer struggle to hide the browser's defects, or should the defects be revealed in hope of creating market pressure to force the browser maker to make good? By which approach is humanity better served?

What I’d Like To Say

When I was a kid, I loved Mad Libs. And I bet you could come up with a Mad Lib that represents a style of question every web developer asks on a regular basis. I think it would look like this:

I’m trying to ______ a ______ and it’s fine in ______ and _______, but in ______ it doesn’t _______ing work.

It’s always the same question - you just have to fill in the blanks with 1 CSS property, 1 DOM element, 3 browser versions, and one profanity. Isn’t it insane? Can’t we start a revolution and all make a New Year’s resolution?

This year I will not write hacky workaround code for defective browsers

But …

It’s a business decision. I think this is what gets under the collective skin of the web development community more than anything else. We can’t make a resolution to stop! We already told the bosses they’ll spend an extra $10,000 to support IE6, but they are more than happy with the return on the investment. So, we sulk back to our offices and grudgingly add some more conditional CSS comments to the site’s style sheet.

If there is going to be market pressure, it won’t come from developers per se, but from big Internet properties with fanatical followers, like Facebook, Amazon, eBay, and Twitter. They have the power to force users to move, and when users start moving the makers react.

And yes, I think it is in the best interest of humanity.

Don&rsquo;t Let Your Code Marry An Axe Murderer

Tuesday, January 12, 2010 by K. Scott Allen

272/365 It’s a lesson I learned from the school of hard knocks: be careful about the shady characters you let into your software.

There are lots of software frameworks, components, and tools in the world – and they all want you to think you can live with them happily ever after. They seduce you with the promise of productivity, then hack you to death once they’ve gained your trust.

Here are a few questions I’ve learned to ask when trying to spot next year’s problems:

1. How easy is it to deploy?

I’ve been burned by software that is difficult to move from one environment to the next. Some products, particularly server products, assume you never need to move software into different environments (like development, test, productions). Avoid anything you can’t automate!

Software should be as easy to deploy as possible, otherwise it doesn’t get deployed often enough.

2. Will it work in source control?

Some tools and designers generate files that don’t work well under source control. If you can’t diff and merge you lose a tremendous amount of flexibility when it comes time to branch, patch, and support concurrent development. Database tools are historically notorious for having problems in this area because they tend to design for DBAs instead of developers.

It is, however, perfectly reasonable to check in binary dependencies.

3. Is it needy?

Speaking of dependencies – they are a touchy subject. You can’t invoke the “not invented here” rule in every decision without writing your own compiler. At the same time, every dependency you introduce to a product is a calculated risk. How many dependencies does this new thing have? Will you ever delay shipping because you are waiting for a new version of a dependency? Will you ever delay shipping because you are waiting for a new version of a dependency’s dependency? How many things will break when the next version arrives? Are you willing to run beta code, or code from the trunk? The answer is different for every application and business.

In the end you need to make a decision from the gut, which means research into the history and pedigree of a dependency might be required.

4. When will it be obsolete?

If you implement the solution to a problem in a mainstream programming language like C#, you can be reasonably sure it will still work and compile in 5 -10 years, even with new versions of the compiler. But, if you implement the same solution with Data Transformation Services (wait, that’s obsolete), or Notification Services (wait, that disappeared), or SQL Distributed Management Objects (oops, it’s only a matter of time) - then you got some rewritin’ to do!

Remember – nothing lasts forever … unless you have all the source code.

More On The Death of If-Else

Sunday, December 6, 2009 by K. Scott Allen

Aaron Feng posted recently on “The death of if-else, if, and else”. In the post Aaron rewrote some JavaScript conditional checks using a dispatch table type approach.

Following along with Aaron’s post using C#, we’d start with a list of Channel objects:

var channels = new List<Channel>
{
    new Channel { Number = 2,
                  Station = "NBC",
                  ShowTitle = "Saturday Night Live",
                  Genre = "comedy",
                  Repeat = true },
    new Channel { Number = 3,
                  Station = "ESPN",
                  ShowTitle = "College Football",
                  Genre = "football",
                  Repeat = false}
    // ...
};

Then we have the logic for deciding which channels to record:

public void Surf(IEnumerable<Channel> channels)
{
    foreach (var channel in channels)
    {
        if (channel.Genre == "football")
        {
            Record(channel);
        }
        else if (channel.Genre == "comedy" && 
                 !channel.Repeat)
        {
            Record(channel);
        }
        else if (channel.Genre == "crime" &&
                channel.ShowTitle != "Cops!")
        {
            Record(channel);
        }
    }
}

Rewriting the code using Aaron’s final approach would look like the following:

public void Surf2(IEnumerable<Channel> channels)
{
    var dispatch = new Dictionary<string, Action<Channel>>
    {
        { "football", c => Record(c) },
        { "comedy",   c => {if(!c.Repeat) Record(c);}},
        { "crime",    c => {if(c.ShowTitle != "Cops!") Record(c);}}
    };

    foreach (var channel in channels)
    {
        dispatch[channel.Genre](channel);
    }
}

Personally, I feel Aaron’s dispatch table is not a big improvement over the previous “if else” version. The actions in the dispatch table are too busy. I think a cleaner approach is to just extract the rules into a data structure – essentially build a collections of predicates to evaluate. Given a channel, the data structure can tell me if the channel should be recorded.

public void Surf3(IEnumerable<Channel> channels)
{
    var recordingRules = new Func<Channel, bool>[]
    {
        c => c.Genre == "football",
        c => c.Genre == "comedy" && !c.Repeat,
        c => c.Genre == "crime" && c.ShowTitle != "Cops!"
    };

    foreach (var channel in channels)
    {
        if(recordingRules.Any(rule => rule(channel) == true))
        {
            Record(channel);
        }
    }
}

Not quite as flexible, but for this specific example its easier to read and maintain.

What do you think? Is that better or worse?

Your Abomination Is My Clever Hack

Tuesday, December 1, 2009 by K. Scott Allen

In the MvcContrib project there is an HTML helper for putting data into a <table>. The syntax looks like this:

<%= Html.Grid(item.Addresses)
            .Columns(column =>
                {
                    column.For(a => a.City);
                    column.For(a => a.Country);
                    column.For(a => a.Street);   
                })
            .Attributes(style => "width:100%")
            .Empty("No mailing address present!")
%>

I remember thinking this API was rather cool when Jeremy Skinner presented the syntax on his blog. The syntax became the topic of discussion in a recent Stack Overflow post - Abuse of C# lambda expressions or Syntax brilliance?

 I'm fascinated, yet at the same time repulsed, by a syntactic trick used in the Grid syntax:

.Attributes(style => "width:100%")

The syntactic trick is how the API uses the name of the lambda’s parameter as the name of the HTML attribute to set. You can set a bunch of the table attributes like this…

.Attributes(style       => "width:100%",
            id          => "grid",
            cellpadding => "0") 

…and it will render …

<table id="grid" style="width:100%" cellpadding="0">
   ....
</table>

Beautiful, don’t you think?

Bending Languages

bend I can appreciate the following thought process:

  1. How did they do that?
  2. Oh. My. Gosh.

I mean - nobody expects the name they pick for a parameter at compile time to be so important during runtime. The Stack Overflow post has a couple choice comments, including two from Eric Lippert:

This is horrid in so many ways

And …

I just asked Anders (and the rest of the design team) what they thought. Let's just say the results would not be printable in a family-friendly newspaper

I don’t think one of the goals of C# is to “be like Lisp”, but I like the code, and it reminds me of a 1993 essay by Paul Graham titled “Programming Bottom-Up”.

Experienced Lisp programmers divide up their programs differently. As well as top-down design, they follow a principle which could be called bottom-up design-- changing the language to suit the problem. In Lisp, you don't just write your program down toward the language, you also build the language up toward your program. As you're writing a program you may think "I wish Lisp had such-and-such an operator." So you go and write it.

The project I’ve worked on for the last part of the year was a huge technical challenge. I bent the hell out of C# to try and make something that was easy to read and maintain. Looking back now, I can see a lot of mistakes and plenty of room for improvement, but I also believe if the project didn’t use some tricks with extension methods and Expression<T>, we’d be facing 3x the amount of code and 3x the complexity.

Since that time I’ve become a big believer in bending languages. You have to look past the tricky implementation details and see the bigger problem being solved.

What&rsquo;s The First Thing To Learn About ASP.NET MVC?

Monday, November 30, 2009 by K. Scott Allen

If you are an ASP.NET Web Forms programmer making the switch to ASP.NET MVC – what’s the first thing you should learn?

There are lots of candidates for the number one spot. Some people will say the first thing to do is learn and embrace the spirit of the MVC design pattern. Others might say you should learn how to unit test a controller, or learn how to use a view model.

fill out your forms! My answer is:

Learn how to use an HTML <form>.

Please excuse the ASP.NET web forms programmer who forgets about the <form> tag. Although every .aspx file has one, they never need to touch it, or even look at it. The form tag is not only well hidden, but also constrained. It only take a few years to start thinking the web revolves around pages that only submit information back to themselves.

The most important things to lean (or relearn) are:

  • You can use the method attribute of <form> to specify the HTTP method to use for  submission (GET or POST).
  • You can use the action attribute of the <form> to submit information to any URL.
  • You can have multiple <form> tags on a single page.

Let’s look at each case.

Method Attribute – GET versus POST

There are two types of requests a web browser will use when communicating with a web server – POST requests and GET requests. Both are designed to do what they sound like – GET a response, or POST some information and receive a response. Web forms always use POST methods. HTTP POST is the best method to use when you are submitting information to a web server and changing state in your application. For example, if a user fills out a <form> with textbox and radio button controls full of patient information you want to save in a database, then POST is the HTTP method you should use. The browser will POST the user’s values to the server where you can save them in the database, and then you can respond and tell the user everything worked (or tell them something blew up).

On the other hand, an HTTP GET operation operation is the best way to submit information to a server when you are not changing state inside the application. The best example is a form used to search for information. You need to send search parameters to the server, but the search parameters aren’t creating new records in a database – they are only used as to query information.

image <form action="/search" method="get">
    <input type="text" name="searchTerm" />
    <input type="submit" value="Search" />
</form>

If the user enters “baby blue bathysphere” into the textbox and clicks the button, the browser will send off a GET request with the following URL:

 /search?searchTerm=baby+blue+bathysphere

Notice how the submitted form values are placed in the URL itself. Not only is a GET operation cacheable, but users can also bookmark the response to a GET submission because all the information needed to produce the page is in the URL. GET is the default value for the method attribute of a <form>, so if you don’t specify the method you’ll have a GET by default.

Actions

ASP.NET web forms always POST to themselves, which can be limiting. As the previous example demonstrates, you can set the action attribute to submit a form to any URL, even a URL pointing to another application on a different server. For example, the following HTML would send the user to Google (both Google and Bing both expect the search parameter to have the name “q”).

<form action="http://google.com/search" method="get">
    <input type="text" name="q" />
    <input type="submit" value="Search" />
</form>

You’ll usually be setting the action attribute to submit information to some controller action in your MVC application. The Html.BeginForm helpers will help build the correct <form> tag to reach the desired action for you. If we were using the first code-snippet in this post (the one with an action of “/search”), we could respond to the search request with the default action of a SearchController. The MVC framework can automatically pass the searchTerm form parameter as a parameter to the action.

public class SearchController : Controller
{
    public ActionResult Index(string searchTerm)
    {
        // ....
        return View();
    }
}

Two Forms Are Better Than One

Well … not always.

There are times when having multiple <form> tags on a page is a good idea. You can have two distinct pieces of a page submit information to two different URLs. Multiple <form> tags regularly make sense on portal type pages that contain multiple sections with disparate functionality. Like an area that lets a user get a stock quote, and an area that lets a user search for a movie. These are two different tasks that will probably best be handled by two different controllers, two different URLs, two different forms, and two different sets of input controls.

<form action="/stock/quote">
    <input type="text" name="symbol" />
    <input type="submit" value="Quote It!" />
</form>

<!-- ... --->

<form action="/movie/search">
    <input type="text" name="q" />
    <input type="submit" value="Search" />
</form>

Not every scenario requires multiple forms, however. Sometimes you want a single form to provoke different behaviors from the server. For example, imagine a shopping page where a user selects some check boxes for products they are interested in. You might need a button allowing the user to submit this information to “compare products” and another button allowing the user to save the selected items in a “wish list”. Both buttons need to submit the same information to the server, so they’ll probably live inside the same form

<form action="products/shop" method="post">
    <!-- -->
    <input type="submit" name="task" value="Compare" />
    <input type="submit" name="task" value="Save to wish list" />
</form>

Both buttons in the above form will force the browser to POST to products/shop. Notice how both buttons have the same value for the name attribute, but their values are different. In an MVC application you can create a controller to accept this form submission:

public class ProductsController : Controller
{
    [AcceptVerbs(HttpVerbs.Post)]        
    public ActionResult Shop(string task)
    {
        // ...
        return View();
    }
}

The value of the task parameter in the Shop action will contain the value of the button – either “Compare” or “Save to wish list”. You can inspect this value and decide what to do next.

Conclusion

Having <form> tags available to due your bidding in an MVC application allows you a great deal of flexibility and control. Every bit you learn about using forms will give you an edge in building your application. Experiment with them, read about them, and learn to love them!