Search code examples
jqueryasp.netasp.net-mvcunobtrusive-validation

Multiple forms on page with jquery unobtrusive validation in ASP.NET MVC


JQuery unobtrusive validation seems to work based on the model passed to the page - but what if I want to have more than one model in play?

Let's say "MyPage" has two forms, each posting back to a different Action, with a different model

Models

public class MyPageModel
{
    public List<Student> Students { get; set; }
    public List<Prof> Profs { get; set; }
    [Required]
    public string Student { get; set; } // wrong wrong wrong
    [Required]
    public string Prof { get; set; } // so wrong. this can't be the way
}

public class AddStudentModel 
{
    [Required]
    public string Student { get; set; }
}

public class AddProfModel 
{
    [Required]
    public string Prof { get; set; }
}

View

// MyPage!

// list students here

@using (Html.BeginForm("AddStudent", "Controller", new { }, FormMethod.Post))
{
     @Html.TextBox("Student", null, new { })
     @Html.ValidationMessageFor("Student")
     <input type="submit" value="add student" />
}

// list professors here

@using (Html.BeginForm("AddProf", "Controller", new { }, FormMethod.Post))
{
     @Html.TextBox("Prof", null, new { })
     @Html.ValidationMessageFor("Prof")
     <input type="submit" value="add prof" />
}

Controller

public ActionResult MyPage()
{
    MyPageModel model = new MyPageModel(); 
    // bind data here
    return View(model);
}

public ActionResult AddStudent(AddStudentModel model)
{
    // add student
    return RedirectToAction("MyPage");
}

public ActionResult AddProf(AddProfModel model)
{
    // add professor
    return RedirectToAction("MyPage");
}

Up to now I've been adding empty Student / Prof properties to MyPageModel, but this feels very hacky. Is there a way to specify a model in the Html.BeginForm that jquery validation will use?


Solution

  • You could use child actions for this, and remove your extra properties from your MyPageModel:

    public ActionResult MyPage()
    {
        MyPageModel model = new MyPageModel(); 
        // bind data here
        return View(model);
    }
    
    [HttpGet]
    public ActionResult AddStudent()
    {
        return PartialView(new AddStudentModel());
    }
    
    [HttpPost]
    public ActionResult AddStudent(AddStudentModel model)
    {
        //add student
        return RedirectToAction("MyPage");
    }
    

    Current MyPage:

    //MyPage!
    @Html.Action("AddStudent", "SomeController")
    
    @Html.Action("AddProf", "SomeController")
    

    New view returned by new child action.

    //AddStudent.cshtml
    
    @using (Html.BeginForm())
    {
         @Html.TextBoxFor(m => m.Student)
         @Html.ValidationMessageFor(m => m.Student)
         <input type="submit" value="add student" />
    }
    

    To make this a slick solution, it might be worth taking a look at Ajax.BeginForm as it'll allow you to update a form at a time on the page (and return a partial view as a reponse so a form submission doesn't need to refresh the whole page).