Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC 3 Remote Validation Form Button Not Included in POST Data

I have an ASP.NET MVC3 app and with remote validation on one of my model classes. I discovered if I open the Edit view for the that model and the remote validation routine IS NOT called (due to not editing the field with remote validation on it) before submitting the form, the button clicked to POST the form IS NOT included in the request data sent to the Action method.

If the remote validation routine IS called before the form is POSTed - the button clicked to submit the form IS included in the POST.

I need to know which button was clicked when the Action is called on the server so I can determine if the user clicked UPDATE or CANCEL.

If I remove the remote validation from the model the form submits correctly every time.

Why would the button that was used to submit the form be excluded from the form post when remote validation is not called prior to the form submission.

thanks Michael

Here is some sample code:

@model Channel
<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>

@using (Html.BeginForm()) {
<fieldset>
    <legend>Edit Channel</legend>

    @Html.HiddenFor(model => model.ChannelGUID)


    <div class="editor-label">Name</div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => model.Name)
    </div>

    <div class="editor-label">Description</div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Description)
        @Html.ValidationMessageFor(model => model.Description)
    </div>

    <div class="editor-label">Channel Code</div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Code)
        @Html.ValidationMessageFor(model => model.Code)
    </div>

    <p>
        <button type="submit" name="updateCommand" value="Update">Update</button>
        <button type="submit" class="cancel">Cancel</button>
    </p>
</fieldset>
}

here is the action

[HttpPost]
public ActionResult Edit(string updateCommand, Channel channel)
{
    if (updateCommand != null)
    {
     //update clicked
    }
    else
    {
     //cancel clicked
    }

here is the remote attribute on the model property Code

[Remote("IsChannelCodeUnique", "Channel", AdditionalFields = "ChannelGUID", ErrorMessage = "code must be unique")]
like image 472
MIantosca Avatar asked Jun 17 '11 13:06

MIantosca


1 Answers

I encountered the same issue. I added remote validation to a form that had two submit inputs and the value of the submit button that was clicked, was not always posted any more.

While the other answers provide work-arounds, I delved it bit deeper and will answer the actual question you asked: "Why would the button that was used to submit the form be excluded from the form post when remote validation is not called prior to the form submission."

I knew that a Javascript Submit does not include the Submit Button Value. Therefore, I tried to post with javascript disabled. Validation didn't work, but the value of my submit was posted. I assumed that jquery.validate.js jumps in and sends a programmatic submit. This is indeed the case. This is what happens, using jquery.validate.js 1.9.0, uncompressed:

  1. When the page is loaded, on line 942 the validators are extended with a remote method, and on line 735 with a stopRequest prototype.
  2. Validation occurs when the input field looses focus for the first time, when you type in it after that or when you submit the form.
  3. Upon validation, the stopRequest method is called.
  4. When the a submit input is clicked, submission is suppressed somehow. in line 56 there is a statement event.preventDefault();, but this is only called in debug mode. Anyway, the stopRequest method will be called when a submit was clicked and after the remote validation was performed.
  5. When the form is valid, line 742 is called: $(this.currentForm).submit();, which is a programmatic submit and will not post the value of the submit. This is why I cannot determine which submit button was clicked.

Looking more carefully at the way the validation is extended, I found on line 59 the definition of a handler that seems to be designed to overcome some similar issue: It adds a hidden input with exactly the name and value of the submit button it handles. However, in my case neither validator.settings.submitHandler nor validator.submitButton are defined, so this code is never called.

It would have been nice if this hidden field were added in this case as well, since MVC will not know nor care what type of input posted the value and the post action on the controller could just use the value.

Addition My workaround for the issue is adding a handler to the click functions of the submits:

$('input[type="submit"]').click(function () {
    var hidden = $("<input type='hidden'/>").attr("name", this.name).val(this.value).appendTo($(this).closest("form"));
});

The hidden field is created, so I do not have to rely on a hidden field with the correct name being present. You can either give your submits the same names, and test for the value, or give them different names and test for the presence of a post parameter. Of course, you would also need to use a form field name not already in use.

like image 57
R. Schreurs Avatar answered Nov 08 '22 12:11

R. Schreurs