Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC: Hidden field value does not get rendered using HtmlHelper.Hidden

Something pretty weird is happening with my app:

I have the following property in my ViewModel:

public int? StakeholderId { get; set; }

It gets rendered in a partial view as follows:

<%= Html.Hidden("StakeholderId", Model.StakeholderId) %>

The form is submitted, and the relevant controller action generates an id and updates the model, before returning the same view with the updated model

The problem I'm experiencing is that the hidden field does not have anything in its "value" attribute rendered the second time even though StakeholderId now has a value.

If I just output the value on its own, it shows up on the page, so I've got it to render the value by doing this:

<input type="hidden" id="StakeholderId" name="stakeholderId" value="<%: Model.StakeholderId %>" />

But it's pretty strange that the helper doesn't pick up the updated value?

(I'm using jQuery to submit forms and render the action results into divs, but I've checked and the html I get back is already wrong before jQuery does anything with it, so I don't think that has much to do with anything)

UPDATE

I've since discovered that I can also clear the relevant ModelState key before my controller action returns the partial view.

like image 446
Veli Gebrev Avatar asked Jan 07 '10 08:01

Veli Gebrev


2 Answers

The helper will first look for POSTed values and use them. As you are posting the form it will pick up the old value of the ID. Your workaround is correct.

like image 152
Darin Dimitrov Avatar answered Nov 15 '22 18:11

Darin Dimitrov


ADDENDUM: Multiple HTML Forms, eg, in a Grid

As an addendeum to this issue, one thing to be VERY careful of is with multiple forms on the same page, eg, in a grid, say one generated using Ajax.BeginForm.

You might be tempted to write something along the lines of:

@foreach (var username in Model.TutorUserNames)
        {
            <tr>
                <td>
                    @Html.ActionLink(username, MVC.Admin.TutorEditor.Details(username))
                </td>
                <td>
                    @using (Ajax.BeginForm("DeleteTutor", "Members",
                        new AjaxOptions
                        {
                            UpdateTargetId = "AdminBlock",
                            OnBegin = "isValidPleaseWait",
                            LoadingElementId = "PleaseWait"
                        },
                        new { name = "DeleteTutorForm", id = "DeleteTutorForm" }))
                    {    
                        <input type="submit" value="Delete" />
                        @Html.Hidden("TutorName", username)
                    }
                </td>
            </tr>
        }

The lethal line in here is:

@Html.Hidden("TutorName", username)

... and intend to use TutorName as your action's parameter. EG:

public virtual ActionResult DeleteTutor(string TutorName){...}

If you do this, the nasty surprise you are in for is that Html.Hidden("TutorName", username) will, as Darin Dimitrov explains, render the last POSTed value. Ie, regardless of your loop, ALL the items will be rendered with the TutorName of the last deleted Tutor!

The word around, in Razor syntax is to replace the @Html.Hidden call with an explicit input tag:

<input type="hidden" id="TutorName" name="TutorName" value='@username' />

This works as expected.

Ie:

NEVER, EVER USE Html.Hidden TO PASS A PARAMETER BACK TO YOUR ACTIONS WHEN YOU ARE USING MULTIPLE FORMS IN A GRID!!!

Final Caveat:

When constructing your hidden input tag, you need to include both name and id, set to the same value, otherwise, at the time of writing (Feb 2011) it won't work properly. Certainly not in Google Chrome. All you get is a null parameter returned if you only have an id and no name attribute.

like image 33
awrigley Avatar answered Nov 15 '22 19:11

awrigley