Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3: Generate unobtrusive validation when BeginForm is on the layout

I just realized that when I place a form tag on my layout page, surrounding the RenderBody section, the unobtrusive validation is not being generated. Something like this:

@using (Html.BeginForm())
{
    <input type="submit" value="save" />

    <div>
        @RenderBody()
    </div>
}

As you might have guessed I want to generate buttons over my content. Is this the correct unobtrusive's behavior?

BTW, If I place the form inside a particular page, everything works like a charm: the data-val* attributes are well generated.

I'll appreciate your valuable help.

best regards

Rodrigo

like image 832
Rodrigo Caballero Avatar asked Feb 14 '11 21:02

Rodrigo Caballero


3 Answers

You could apply a grotesque hack inside your view:

@{
    var originalContext = ViewContext.FormContext;
    ViewContext.FormContext = new FormContext();
}

<!-- This will generate proper HTML5 data-* validation attributes -->
@Html.TextBoxFor(x => x.Prop1)
@Html.ValidationMessageFor(x => x.Prop1)

@Html.TextBoxFor(x => x.Prop2)
@Html.ValidationMessageFor(x => x.Prop2)

@{
    ViewContext.FormContext = originalContext;
}
like image 57
Darin Dimitrov Avatar answered Nov 25 '22 09:11

Darin Dimitrov


While putting @using (Html.BeginForm()) into the content page fixes the validation problem it also puts an extra set of <form> tags into the output. I created a small extension that fixes the problem without writing anything to the output.

Use it as @using (Html.BeginSubForm())

public static class FormExtensions
{
    public static MvcSubForm BeginSubForm(this HtmlHelper html)
    {
        return new MvcSubForm(html.ViewContext);
    }
}


public sealed class MvcSubForm : IDisposable
{
    private readonly ViewContext _viewContext;
    private readonly FormContext _originalFormContext;

    public MvcSubForm(ViewContext viewContext)
    {
        _viewContext = viewContext;
        _originalFormContext = viewContext.FormContext;

        viewContext.FormContext = new FormContext();
    }


    public void Dispose()
    {
        if (_viewContext != null)
        {
            _viewContext.FormContext = _originalFormContext;
        }
    }
}
like image 41
Aidan Boyle Avatar answered Nov 25 '22 09:11

Aidan Boyle


Thanks for your help, I tried it but I found a solution not as "grotesque" (as you said) as you suggested :D

I simply put a BeginForm method inside my page and also a BeginForm method on the layout:

@* On the layout page *@
@using (Html.BeginForm())
{
    <input type="submit" value="save" />

    <div>
        @RenderBody()
    </div>
}


@* On the content page *@
@using(Html.BeginForm())
{
  @* Content *@
}

so, at the end I have two BeginForm methods: ASP.NET MVC engine is using the one located on the layout page, so the data-val* attributes are being rendered correctly and the form is placed just where I wanted so any submit button on the layout can submit my particular page with the validations rendered

It works pretty well

Thanks a lot

regards, Rodrigo

like image 39
Rodrigo Caballero Avatar answered Nov 25 '22 09:11

Rodrigo Caballero