views:

456

answers:

4

I have an HTML table where each row has buttons which toggle status bits in the database for each row. Assuming Javascript is not an option, what would be the "best practice" way of handling this?

I'm currently handling it by wrapping each row in a form like this:

<table>
    <tr>
        <td>
            <form action="/FooArea/BarController/BazAction" id="frm0" name="frm0" method="post">
                 <span>Item 1</span>
                 <input type="submit" value="Toggle1" name="submitButton"  />
                 <input type="submit" value="Toggle2" name="submitButton"  />
                 <input type="hidden" name="itemID" value="1" />
            </form>
        </td>
    </tr>
    <tr>
        <td>
            <form action="/FooArea/BarController/BazAction" id="frm1" name="frm1" method="post">
                 <span>Item 2</span>
                 <input type="submit" value="Toggle1" name="submitButton"  />
                 <input type="submit" value="Toggle2" name="submitButton"  />
                 <input type="hidden" name="itemID" value="2" />
            </form>
        </td>
    </tr>
</table>

And the post method looks something like this:

string buttonName = Request.Form["submitButton"];    
if (!String.IsNullOrEmpty(buttonName )) 
{
     int itemID = Convert.ToInt32(Request.Form["itemID"]);
     switch (buttonName )
     {
         case "Toggle1":
         DoSomething(itemID);
         break;

         case "Toggle2":
         DoSomethingElse(itemID);
         break;
     }
}

Any better suggestions? Is having 50 forms on a page cool? I dunno.. let me know what you guys are doing in this case.

A: 

This seems like a prime case for some JQuery and Ajax functionality. But if javascript isn't an option then i think wrapping each one in a form is probably the most simple solution.

Patricia
Thanks, yea I'm trying to figure out if there's something I'm overlooking. I'm not convinced this is the most elegant solution, but if it's the way to do it, then hey... ;)
Ozzie Perez
A: 

When I need to do something like this I do it pretty much the same way as you do here. It is no problem to have multiple forms in one view. Its like having multiple links in one view (but they use POST instead of GET). I guess many .net developers think its wrong because of webforms only having one form tag. In your case I would even create more forms for each submit button as they seem to do different things. I consider having to check the name of the button in the controller to be a code smell. If they do something like activate/deactivate you should probably post that as a bool or something and have both forms post to the same action.

Mattias Jakobsson
You're absolutely right that I've gotten used to 1 form per page, so seeing 50 in one strikes me as shocking. Thanks for your feedback Mattias.
Ozzie Perez
P.S. I had also edited the values from Activate/Disable for the reason you pointed out. I saw that one coming... hehe.
Ozzie Perez
Yes, I can understand that. I felt the same way when I migrated from webforms. Even if you are going to do this with javascript and ajax I would probably add those forms anyway. That way it will work when javascript is disabled in the browser and its pretty easy to use some jquery ajax form plugin.
Mattias Jakobsson
@Ozzie, Phil Haack has written a nice javascript thing that uses a standard link for POST's if javascript is enabled, and has a form as fallback. Might be worth taking a look... http://haacked.com/archive/2009/01/30/delete-link-with-downlevel-support.aspx
Tomas Lycken
@Tomas, That's another interesting approach. It's pretty wild, in that it's essentially doubling functionality code-wise, but is the right thing to do to degrade gracefully. I'm appreciating Html Helpers more and more. Thanks again for sharing!
Ozzie Perez
@Ozzie, There is not much need for code duplication if you combine the two - you can use the `KeyValuePair`s to generate the data string for the javascript request as well, so that's not really a problem. As far as functionality duplication goes, there will always be duplication of functionality if you want a nice feature that not everyone supports with a safe fallback.
Tomas Lycken
@Tomas, Yea, in regards to functionality duplication, I guess there's no other way around it. But it's definitely nice that they can be combined into one call using helpers. Thanks again for sharing your code man!
Ozzie Perez
@Ozzie, Btw, you should do something about your accept rate... ;)
Tomas Lycken
@Tomas, ha! I never paid attention to this until after just reading up on it now.. thanks for the heads up. :)
Ozzie Perez
A: 

I think technically this is the best way to go. From a usability point of view you can question if this is the best way to do it. More than 20 items in a list can get quite confusing. But ofcourse, I don't really know what you are programming :)

I agree with Mattias about creating different actions for the activate and deactivate-button. This way you avoid having to use a switch-statement.

Pbirkoff
+1  A: 

The best way to handle POST scenarios without JavaScript is, as several others have stated, a tiny form with only the necessary values, and only one submit button. Basically, what you should do is to create a helper method that creates a form with the necessary POST values in hidden fields and a submit button. For example, you could have a method you use like this:

<%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", new List<KeyValuePair<string, string>>{ new KeyValuePair<string, string>("itemId", 1), new KeyValuePair("action", "option1") }); %>

It looks pretty verbose, but I've tried to make it as generic as possible. You can probably create the List<KeyValuePair<string, string>> in the controller when you render the view, so you only have to call something

 <%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", Model.Values) %>

In the action method that handles the post, you bind to the posted FormCollection, and retrieve the values of itemId and action to determine what to do, instead of checking for Request.Form values.

An implementation of the helper method might look like this:

public static string PostLink(this HtmlHelper helper, string postAction, string submitText, IEnumerable<KeyValuePair<string, string>> postValues)
{
    var form = new TagBuilder("form");

    // Setup basic properties like method, action
    form.Attributes.Add("method", "post");
    form.Attributes.Add("action", postAction);

    // Instantiate a stringbuilder for the inner html of the form
    var innerHtml = new StringBuilder();

    // Create and append hidden fields for the post values
    foreach(var value in postValues)
    {
        var hidden = new TagBuilder("input");
        hidden.Attributes.Add("type", "hidden");
        hidden.Attributes.Add("name", value.Key);
        hidden.Attributes.Add("value", value.Value);
        innerHtml.Append(hidden.ToString(TagRenderMode.SelfClosing));
    }

    // Create the submit button
    var submit = new TagBuilder("input");
    submit.Attributes.Add("type", "submit");
    submit.Attributes.Add("value", submitText);
    // Append it to the stringbuilder
    innerHtml.Append(submit.ToString(TagRenderMode.SelfClosing));

    // Set the InnerHtml property of the form, and return it
    form.InnerHtml = innerHtml.ToString();
    return form.ToString(TagRenderMode.Normal);
}
Tomas Lycken
Creating an HTML Helper form that takes the hidden fields is a very interesting way to go about it. I like it. Especially since we can pass the set of hidden inputs from the controller and simplify the whole deal. :) Thanks for sharing this!
Ozzie Perez