Multiple postbacks in an MVC page

With my limited (but growing) experience in MVC, I’ve come up with a way to handle multiple postbacks to the same page in MVC. Here’s a simple application:

  • User selects a category from a dropdownlist – causes postback and loads subcategories
  • User selects a sub category and clicks on the submit button
  • A confirmation message appears on the view

Here’s how I’ve implemented it. Created an MVC project and added two classes – Category and SubCategory:

   1:      public class Category
   2:      {
   3:          public int CategoryID { get; set; }
   4:          public string CategoryName { get; set; }
   5:      }
   6:   
   7:      public class SubCategory
   8:      {
   9:          public int SubCategoryID { get; set; }
  10:          public string SubCategoryName { get; set; }
  11:          public int CategoryID { get; set; }
  12:      }

In the HomeController’s Index method (GET version):

   1:  public ActionResult Index()
   2:  {
   3:      var categories = GetCategories();
   4:      var subCategories = GetSubCategories(0);
   5:   
   6:      ViewData["PreviousCategoryID"] = 0;
   7:   
   8:      ViewData["CategoryID"] = new SelectList(categories, "CategoryID", "CategoryName");
   9:      ViewData["SubCategoryID"] = new SelectList(subCategories, "SubCategoryID", "SubCategoryName");
  10:      return View();
  11:  }

The GetSubCategories(0) returns a dummy list taking zero as the default value for the categoryID. I’ll let you know what the ‘PreviousCategoryID’ is in a few. My View looks like this:

   1:  <h2>Movie Guide</h2>
   2:  <% using(Html.BeginForm()) { %>
   3:      <%= Html.Hidden("PreviousCategoryID", ViewData["PreviousCategoryID"]) %>
   4:      <p>
   5:          <%= Html.DropDownList("CategoryID", (SelectList)ViewData["Categories"], 
   6:                                new { onchange = "this.form.submit();" })%>
   7:          <p />
   8:          <%= Html.DropDownList("SubCategoryID", ViewData["SubCategories"] as SelectList)%>
   9:          <p />
  10:          <input type="submit" value="Submit" />
  11:          <p />
  12:          <%= ViewData["Message"] %>
  13:      </p>
  14:  <% } %>

So, the user selects a category from the list and post back occurs. Now, here’s the issue I want cleared off.

In order to determine which control caused the postback, in my Index method (POST version), I check the value of the CategoryID dropdownlist against the PreviousCategoryID. If they are different, I know the postback was caused by the dropdownlist and I populate the SubCategories list based on categoryID.

However, if they are same, I assume the postback was caused by the button click and hence I display the message to the user.

Is this the best way to do the check or am I missing something?

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public ActionResult Index(FormCollection formCollection)
   3:  {
   4:      var categoryID = int.Parse(formCollection["CategoryID"]);
   5:      var previousCategoryID = int.Parse(formCollection["PreviousCategoryID"]);
   6:   
   7:      // if these are different, then postback was caused by the category dropdownlist
   8:      if(categoryID != previousCategoryID)
   9:      {
  10:          var categories = GetCategories();
  11:          var subCategories = GetSubCategories(categoryID);
  12:   
  13:          ViewData["PreviousCategoryID"] = categoryID;
  14:          
  15:          var categoryList = new SelectList(categories, "CategoryID", "CategoryName", categoryID);
  16:   
  17:          ViewData["Categories"] = categoryList;
  18:          ViewData["SubCategories"] = new SelectList(subCategories, "SubCategoryID", "SubCategoryName");
  19:          return View();
  20:      }
  21:      // if not, assume a button click and show message to user
  22:      else
  23:      {
  24:          var categories = GetCategories();
  25:          var subCategories = GetSubCategories(0);
  26:   
  27:          ViewData["PreviousCategoryID"] = 0;
  28:   
  29:          ViewData["Categories"] = new SelectList(categories, "CategoryID", "CategoryName");
  30:          ViewData["SubCategories"] = new SelectList(subCategories, "SubCategoryID", "SubCategoryName");
  31:          ViewData["Message"] = "Your selected has been noted.";
  32:          return View();
  33:      }
  34:  }

Line 8 is where I check for the difference. If true, PreviousCategorID gets updated with the latest categoryID (line 13) and I can use it in the next postback.

This no doubt works, but seems a little ‘icky’ (sorry for the choice of words.. English IS my second language). Please let me know if there’s a better way to handle this situation.

Thanks
Arun

8 Comments

  • You also could have checked for the presence of your button (give it a name) in the form collection.

    You could also have your dropdownlist javascript change the action attribute of the form to point to another action (thus executing a different method). Triggering another action (in whatever way) would be my first choice.

  • You could use if(!string.IsNullOrEmpty(formCollection["submit"])) to determine if page is submitted by clicking submit button.

  • I think you would be better off using jQuery Ajax to load the subcategory drop down lists. Much better user experience and probably easier anyway.

  • I agree with Craig. You would be much better off using JQuery to dynamically populate the second drop down. Combine it with the Select box plug-in (http://www.texotela.co.uk/code/jquery/select/) and it is a snap

  • Thanks for the input you guys

  • I think that you should split your action in two different ones: maybe SelectSubcategories and Submit or something like that. Since you relly on js changing the Action for the form is pretty simple.

    Both branches of the if statement are practically identicals but are related to different "actions", use extract method to create a private helper method for the common functionality.

    If you can't do that check for the submit button to be present, it will make the code more expressive. That's the way we used to do it in classic asp and php.


  • You definitely should be using 2 actions with 2 form tags. Whether you use Ajax or not, you are requesting 2 pieces of data and you should not be using 1 action for this. Your controller should not have to "know" which button or drop-down or whatever was selected.

  • Developers will want to code in their preferred lnuaagge and C# is the dominant .net lnuaagge, thus the need for a better web framework written in C#. Ruby has Ruby on Rails, and Python has Django.Software is never completely original and is always borrowing and sharing ideas from other code and frameworks. I guess that Microsoft believe that they can improve upon whats out there, and take what already works from the other awesome frameworks such as RoR, Django And MonoRail.In terms of tools its up to the developers to be aware of whats right and whats disgusting, and make better decisions from reading blogs!

Comments have been disabled for this content.