Thursday, 8 May 2008

Using ASP.NET MVC HtmlHelper.Form

Today, I tried making a HTML form with the ASP.NET MVC HtmlHelper, but it took me a while to get it working as documentation on the web is a little sparse at the moment. Here's some basic instructions.

View

In the view, you can use a using statement to ensure the Form tag is closed properly. I am using the generic version of the HtmlHelper.Form method, so that I can easily specify a method on my controller to be called when the form submit button is clicked. This method can take parameters if you want (for example the ID when creating an edit form), but it should not have parameters for the actual input fields on the form - they will be read out of the Request object by the controller.

<h2>New Post</h2>
<% using (Html.Form<BlogController>(c => c.CreatePost()))
  { %>
<label for="postTitle">Title</label>
<%= Html.TextBox("postTitle") %>
<br />
<%= Html.TextArea("postBody","", 10,80) %>
<%= Html.SubmitButton("submitButton","Save") %>
<% 
  } %>

Controller

The controller method, as I have already indicated does not need to take the form inputs as parameters. It reads these out of the Request.Form dictionary.

public ActionResult CreatePost()
{
   string postTitle = Request.Form["postTitle"];
   string postBody = Request.Form["postBody"];   
   Post post = new Post { Title = postTitle, Body = postBody };
   post.Status = PublishStatus.Published; 
   blogService.CreatePost(post);
   return RedirectToAction(new { action = "Index" });
}

There is a Binding.UpdateFrom helper method that you can use to speed up the code further if you name your fields correctly.

Testing

It's fairly easy to test your controller's new method. Simply add references to System.Web and System.Web.Abstractions and you can populate the Request.Form dictionary before calling your action method:

[Test]
public void BlogControllerCanCallCreatePost()
{
   BlogController blogController = new BlogController(new TestBlogRepository());
   blogController.Request.Form["postTitle"] = "Title";
   blogController.Request.Form["postBody"] = "Body";
   ActionResult result = blogController.CreatePost();
}

EDIT: Looks like I spoke too soon. The above test code will actually fail because blogController.Request is null. I'll update this post once I have worked out an easy way to populate the Request.Form dictionary. And in future I'll try to remember to actually run my unit tests before declaring them a success!

Post a Comment