Amadiere.com

Fourteen and a half crazy frog burpers

30th August 2011

ASP.NET MVC 3: Drop Down Lists / SelectLists

Filed under: JQuery,MVC — Tags: , — Alex Holt @ 10:42 pm

There are a number of very useful helpers that come as default with the ASP.NET MVC framework, but one that always seems to get people confused is the Html.DropDownListFor() helper method. So in this post, I’ll quickly go over the steps I use to populate the list, as well as some of the more funky features once it’s up and running!

In our example, there will simply be a single drop down list on a form that shows a list of countries, from which you can select and submit.

First, we need to build a view model that is going to be the contract that determines the data that the view can display.

1
2
3
4
5
6
7
8
9
public class IndexViewModel
{
    // Stores the selected value from the drop down box.
    [Required]
    public int CountryID { get; set; }
 
    // Contains the list of countries.
    public SelectList Countries { get; set; }
}

Once we’ve sorted that, we can create the action result methods for our index page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        IndexViewModel viewModel = new IndexViewModel();
        viewModel.Countries = new SelectList(GetCountries(), "ID", "Name");
        return View(viewModel);
    }
 
    [HttpPost]
    public ActionResult Index(IndexViewModel viewModel)
    {
        viewModel.Countries = new SelectList(GetCountries(), "ID", "Name");
        if (!ModelState.IsValid)
            return View(viewModel);
 
        //TODO: Do something with the selected country...
        CMSService.UpdateCurrentLocation(viewModel.CountryID);
 
        return View(viewModel);
    }
}

You’ll notice there are two instances where we populate the Countries property on the viewModel, one in the HttpGet method and the other in the HttpPost. We do this so that the list of countries is available throughout our process. You see, unlike the CountryID which is submitted as part of the Request object (it’s part of the form collection), our list is never stored – it’s lost as soon as the user navigates away from that page. So we need to repopulate it. In reality, you may only need to repopulate it when the validation fails an you have to redraw the screen in the HttpPost method, so that could be moved.

Then, it’s just a simple case of adding this to my view:

@Html.DropDownListFor(x => x.CountryID, Model.Countries)

Or you could forcably add a blank value to the top with some “please select me, pretty please”-type text. e.g.

@Html.DropDownListFor(x => x.CountryID, Model.Countries, "- please select -")

And there you have it! A working drop down box using MVC 3 and Razor views.
Now for some more fancy stuff. If you’re interested in styling your menu, then you can’t really go far wrong with jQuery UI SelectMenu (of which there are a few demos available, as well as a separate tutorial). To set up, you only need to do a few things:

  • Download JQuery & JQueryUI and add to your solution. You can now do this step through NuGet, or manually by “Add Existing Item” and downloading from the respective site.
  • Add a reference to the .js and .css files to the header of your html header, often set by the _Layout.cshtml file. Something like the following:
   <link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" />
   <script type="text/javascript" src="@Url.Content("></script>
   <script type="text/javascript" src="@Url.Content("></script>
  • Download and add the SelectMenu .js and .css files in much the same way.
<link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/selectmenu.css")" />
<script src="@Url.Content("~/Scripts/selectmenu.js")" type="text/javascript"></script>
  • Then all that lies to do is to set it off going:
<script type="text/javascript">
   $('select').selectmenu();
</script>

The last bit we added there simply makes this apply for every single select box that is on the page. As an aside, when doing AJAX or JQuery where you may potentially add an additional drop down list, you will need to call .selectmenu() to ensure that your new box appears like the others. It’s also worth noting that you may find if your page isn’t a fast loader, the standard drop down box (unstyled) will appear for a brief period.

20th June 2011

Unobtrusive JavaScript in ASP.NET MVC3 with JQuery

Filed under: ASP.NET,C#,HTML,JQuery,MVC — Tags: , , , , , — Alex Holt @ 9:45 pm

I’ve recently experimented a little bit with some of the new unobtrusive JavaScript stuff that is now included as part of the default ASP.NET MVC 3 project template. While not an overly complex subject, it’s been one of them plugins I wanted to have a dabble with, but I didn’t quite find the time until now. And I must say, it was a lot easier to sort out than I thought it might be! I should point out that although this is an ASP.NET MVC 3 example, the concepts and the fact that this is mostly just JQuery means that it’s not limited to that, earlier versions of ASP.NET MVC can use happily, as can WebForms, PHP, Ruby and even Classic ASP!

Some things we’ll try and do in this blog post:

  • Setup a simple form with server-side validation of input fields which redirects to a ‘Success’ page or re-renders the form depending on whether it was successful or not.
  • Add JQuery validation for simple client side validation to the form.
  • Add Unobtrusive JavaScript support which will submit the form via AJAX.

Creating a basic application:

Firstly, I created a ASP.NET MVC 3 Web Application (Empty) in VS2010 and got to work updating the JavaScript libraries that were in it. This can be done via the NuGet Console, or by right clicking “References” in the Solution Explorer, “Add Library Reference” and then clicking the update side-tab. From there you can update all the default stuff.

Done that? Good. Next we’ll add a default controller, a view model to pass the data around & two views (one for allowing us to edit the form and the other as a success page). It’s worth noting that I’m doing nothing that special on these and that I’m using the default _Layout.cshtml to apply the header and footer as it sees fit.

/Controllers/BlackCoffeeController.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Web.Mvc;
using UnobtrusiveAjaxExample.ViewModels.BlackCoffee;
 
namespace UnobtrusiveAjaxExample.Controllers
{
    public class BlackCoffeeController : Controller
    {
        [HttpGet]
        public ActionResult Rocks()
        {
            RocksViewModel viewModel = new RocksViewModel();
            return View(viewModel);
        }
 
        [HttpPost]
        public ActionResult Rocks(RocksViewModel viewModel)
        {
            if (!ModelState.IsValid) return View(viewModel);
            // Do something Database-y here.
            return View("Success");
        }
    }
}

/ViewModels/BlackCoffee/RocksViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.ComponentModel.DataAnnotations;
 
namespace UnobtrusiveAjaxExample.ViewModels.BlackCoffee
{
    public class RocksViewModel
    {
        [Required]
        public string Forename { get; set; }
 
        [Required]
        public string Surname { get; set; }
 
        [Required]
        public string Message { get; set; }
    }
}

Then the two views, firstly: /Views/BlackCoffee/Rocks.cshtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@model UnobtrusiveAjaxExample.ViewModels.BlackCoffee.RocksViewModel
 
@{
    ViewBag.Title = "Rocks";
}
<h2>Black Coffee Rocks?</h2>
<div id="content">
@Html.ValidationSummary()
@using(Html.BeginForm())
{
<ul>
	<li>
            @Html.LabelFor(x => x.Forename)
            @Html.EditorFor(x => x.Forename)
            @Html.ValidationMessageFor(x => x.Forename)
        </li>
	<li>
            @Html.LabelFor(x => x.Surname)
            @Html.EditorFor(x => x.Surname)
            @Html.ValidationMessageFor(x => x.Surname)
        </li>
	<li>
            @Html.LabelFor(x => x.Message)
            @Html.EditorFor(x => x.Message)
            @Html.ValidationMessageFor(x => x.Message)
        </li>
</ul>
<div><input name="Continue" type="submit" value="Continue" /></div>
}
</div>

And then /Views/BlackCoffee/Success.cshtml

1
2
3
4
5
@{
    ViewBag.Title = "Success";
}
<h2>Success</h2>
<p>Congratulations! You've successfully declared BLack Coffee as being totally rockin'!</p>

At this point now, you should be able to build and run your application and navigate to /BlackCoffee/Rocks and verify that the page redraws the form unless all boxes are filled in. If you do fill them all in, you should get the success page.

Adding JQuery Validation with barely any effort for standard Data Annotations.

With certain DataAnnotations such as the [Required] attribute and [StringLength], you should be able to get simple JQuery client side checks, which while not a replacement for server side checks, add some speedy feedback which will be especially welcomed by users who find themselves struggling over long distance or slow connections. The key point to note here is that the checks are performed both sides (client and server) – so it doesn’t matter if the user has JavaScript disabled or is using a browser not capable of running it – the checks will still get done. It’s just an added bonus.

To do, simply add references to the JavaScript files to your html’s <head> tag. In this case, I’m going to add them directly to the _Layout.cshtml file.

6
7
8
9
    <script src="@Url.Content("~/Scripts/jquery-1.6.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript">></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

You’ll spot that there are 4 included files there – the last one (the JQuery.unobtrusive-ajax.min.js) is for the next part – but we’ll put it in now in preparation.

You should find that the Web.Config file already has the following lines in already (specifically lines 10 & 11), but have a gander for the following and if it doesn’t exist or is set different to this, update the file appropriately.

8
9
10
11
12
<appSettings>
    <add key="webpages:Version" value="1.0.0.0"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

We’re now cooking with gas! Save, Build and try out your application. You should be able to spot that when clicking your submit button, the page doesn’t go away to the server at all to check the field validates, it’s doing it on the client. Good stuff!

Adding Unobtrusive AJAX into the mix.

This should be a relatively easy process, but there are a few things we need to consider sorting out before we continue too much further:

  • Update the views to make the Form work via AJAX (without breaking non-JavaScript/AJAX support)
  • Update the controller to detect whether the request is an AJAX request and if so, avoid sending the header / footer / other stuff back with it.

The first part is quite simple. Within the Rocks.cshtml we created earlier which is our edit form view, we used the HtmlHelper class to create the form tag. All we need to do now is change that to use the AjaxHelper class and pass in some options and we’re off to a winner!

10
@using(Ajax.BeginForm(new AjaxOptions() { HttpMethod = "Post", UpdateTargetId = "content" }))

You should be able to see there that there is AjaxOptions class “newed up” directly in the view. If you’re uncomfortable with doing this, you could create it directly in the view model and pass it in from there. There are a ton of options that you can go into, most of which are touched upon on Brad Wilson’s blog post on this subject (if you haven’t read Brad Wilson’s blog, you probably should add it to your reader – it’s exceptional and includes a good chuck of stuff relating to ASP.NET MVC and related topics). You’ll also spot we provide a UpdateTargetId – this represents the HTML element into which all the view result is going to get dumped into. In this case, our DIV on our Rocks.cshtml page – but this could be anything and in fact nothing, if you don’t want to spew out content back to the user for whatever reason.

If you ran the application now, you’d find that all the AJAX and the JQuery would work great, but as part of the Ajax Request, the header and footer would be returned and very soon you’d end up with a webpage that looks like it’s been designed by someone who’s been watching Inception too much. This thankfully (or more specifically thanks to the ASP.NET team), isn’t a problem as we can do a little check before returning the view while in the appropriate controller method. Using the example above, you’d be able to change it to something like this:

15
16
17
18
19
20
21
22
        [HttpPost]
        public ActionResult Rocks(RocksViewModel viewModel)
        {
            if (!ModelState.IsValid) return View(viewModel);
            // Do something Database-y here.
            if (Request.IsAjaxRequest()) return PartialView("Success");
            return View("Success");
        }

You’ll spot with this example we are returning exactly the same view. However, by returning it as a Partial view, we are ensuring it does not get rendered as part of a bigger page, so all of the headers / footers and other things included via layout pages are ignored.

Other things you can do to improve stuff:

There are a plenty of other very easy things you can add too, such as:

  • Loading image: Simply provide it with a content div to display while loading (and a timeout if you want) and in will show() that while that particular AJAX call is made. Meaning that if you want to, you can lock the UI or provide visual feedback that the users request is currently in progress.
  • There are four callback methods you can set for OnBegin, OnSuccess, OnFailure and OnCompleted which will trigger as appropriate. This allows you to add all sorts of extra functionality to the application in those instances you want to do more than simply “add some HTML to a div”. However, two things to note regarding these function calls:
    • The functions you declare should not live inside of the JQuery document.ready() method or other shorthand notation for that. Just plain functions in a .js file is fine (though, you can of course, still use JQuery – that’s not a problem).
    • You might find that the $(this) object within the JavaScript is not populated by the time you get to the methods. You can solve this to some degree by changing the following line from:
      244
      
              options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

      to:

      244
      245
      
              options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });
              options.context = element;

      If I’m honest, I don’t know why this line is missing from the file in the first place. I can’t imagine it’s just been forgotten as it seems like such a key thing to be able to trigger events based on the click events (especially on pages where there are lots of controls which may call the AJAX request). I suspect there is a good reason – but I don’t know it yet.

  • You can use this on more than just <form> tags. For example, it’s just as easy to use the Ajax.ActionLink method which works in a very similar way and has some of your favourite overloads still available!
  • As mentioned at the top, this is mostly just JavaScript. If you wanted to do this with any other server side technology, all you’d need to do is replace the AjaxHelper methods with your own way of outputting the specific HTML “data-” based attributes and you’re up and running!

The full source code is available in a zip file here – though, as you can see above – it’s not a great deal different to the bog standard project!

20th November 2010

OpenID is NOT an ex-parrot!

Filed under: Human Factors,Usability,Websites — Tags: , — Alex Holt @ 2:01 pm

I’ve read a few Tweets recently that OpenID might be dead. Poppycock! OpenID is alive and kicking and as strong, if not stronger, than ever before.

An ex-parrot

What is OpenID? OpenID is a means of identifying that someone who’s visiting your site is the same person as someone who’s already been here. The was traditionally with Usernames and Passwords. If you successfully signed in, we assumed you were the same person. OpenID provides that level of authentication. It basically sends back a little message from the provider (e.g. Google) saying “Yo dude, not spoken in a while, but this customer you asked me about? Well it’s <INSERTGUIDHERE>. Just go ahead and log them straight in or create them a new account”. I now don’t need to store passwords as a website – this is great news!

Rob Conory (of Tekpub) recently wrote a very excellent article “Open ID Is A Nightmare” in which he proceeds to outline his case for why OpenID is turning into a nightmare for him. It’s an entertainingly written article in Rob’s quirky style that outlines his viewpoints as both a developer and as a business owner. The latter point, as he goes on to say, is the key criteria in why he’s reached the point he’s at at the moment.

A friend of mine was offering a ton of solutions to my Open ID woes over Skype the other day – insisting that it’s worth “investing in for the long run – the kinks will get worked out”. I sort of agree as a dev – as a business owner I couldn’t give a rat’s ass.

He is smart guy and has a number of very relevant points that anyone considering using OpenID should be thinking about. He had a number of OpenID unrelated issues (RPXNow downtime), but they all contributed to the main gripe he had about things: customers were unable to get into his site, a site they’ve paid to get access to. This is a key point. People are PAYING to watch the content over at Tekpub and through no fault of his own, they were unable to get in. The problem is easily wafted away, waving your hands in the air and saying “this is a user problem!”. The user created an account and then forgot which provider they logged in with in the first place! What fools! Let us all gather around a camp fire, eat sausages and laugh at their pathetic attempts at life. Except – lets not forget Tekpub’s audience. It’s techies. It’s developers. It’s people like us. If WE are capable of getting confused and lost as to what is going on – then the anecdotal ”mum” is going to struggle a hell of a lot more.

The bonus and one of the driving forces behind OpenID is the supposed “reduced friction” when creating an account with a site. As Rob points out, there is the issue because we can effectively get so little back from the OpenID Provider, that we’d be unable to dig out the account from the mass of other accounts with no additional data. The users account is effectively orphaned until they remember the provider they signed up with. Locked out and angry.

This debate took a whole other level of LOL when Scott Hanselman tweeted:

Is OpenID dead? Poor usability to blame?

For some bizarre reason, the Twittersphere decided that that was neither a question, but an official announcement from Microsoft that OpenID was about to fail. When in reality, it was in relation to Rob’s above post. Humourous to those unaffected, but I can imagine quite irritating and stressful to Scott.

What is wrong with OpenID?

OpenID is fantastic at doing what it’s does, but there is definitely room for improvement. Maybe not in OpenID itself, but in the way it is implemented. Some of the key things that I personally think developers need to consider when designing a login system that uses OpenID are:

  • Each Account should be able to have more than one Login method attached to it.
    This is something I think people have inherited from years gone by. Traditionally, an account has a Username and a password, and nowadays maybe an OpenID field too. In reality, the user should have the ability to add whatever authentication methods your site allows, to their account. A one-to-many relationship from Accounts to Logins. E.g. I might sign up with my Google account, then decide later that I want to create a Username and Password for whatever reason. Then a bit later, I spot you have Facebook Connect as an option – cool, I’ll attach my Facebook account to this site too. Now where am I? I have 3 ways of getting into my account. That’s great news – that’s a lot easier for me as as long as I guess one of them randomly when I’m prompted – I’m sorted.
  • Upgraded accounts require more information.
    The low barrier to entry should be set for standard users, but the minute you decide you want to upgrade them (make them a moderator or take payment for a service from them), you should require more information to make sure you can trace things when issues arise. Requiring this would have given Rob the facility to offer a “forgotten your password?” function on his login page that would solve the issue that paying customers couldn’t get in.
  • The ability to merge accounts.
    Unfortunately, people have lots of different accounts around the internet. They will probably, as Rob did when trying to access StackOverflow, get into your site using the wrong one and in the process, create a brand new account. In theory, what they wanted to do was log in as the correct one and then maybe add this new provider to their list of available logins. This is a tricky problem to solve, but it’s doable. You want to offer the option for a user to merge their two accounts, copying whatever appropriate data to their primary account. If you can achieve this, then you’re giving the users the ability to solve their own problems – great! And even if they don’t, you have a nice UI for doing it on their behalf.
  • “WTF does OpenID mean?”
    Seriously. I’m have no ready solution for this problem – but people don’t know what OpenID is and that’s because it’s techie. We probably need to drop the OpenID words from headings to merely part of the description for clarification, when displaying our Authentication Options. People should just be offered the choice of which site they want to authenticate via: Google, Yahoo!, Facebook or whatever. The issue still remains that people don’t exactly understand still what it means that they are being “authenticated by Facebook”. Half of them won’t care that they don’t know, but for the other half, you probably want to re-assure them of what it is that they are allowing you to do. I don’t want some random site I’ve just stumbled upon telling everyone on Facebook that I’ve done XYZ on website ABC. I think the OpenID selector is good – but is it perfect? Not really – it works for me and is very simple to use – but it doesn’t answer any questions you might have as a customer. As a developer, you need to do that. You need to ease their fears.

If I was creating a site now, would I use OpenID? ABSO-FRICKIN’-LUTELY! It’s great and makes it so easy to actually log into to a site. If I can transfer that ease-of-access to the customer, while not freaking them out – then I’m onto a winner! Is OpenID an ex-parrot? Not at all – it’s here for the foreseeable future in my opinion. And that is a great thing!

Older Posts »

Theme designed & built for Amadiere.com by Alex Holt. Powered by WordPress