0

Extension to: How do you handle multiple submit buttons in ASP.NET MVC Framework?

Let us say a view is composed of partial views bound with related models, let's say a student is required to provide multiple contact persons(partial view bound to Person model) and multiple contact numbers(partial view bound to a model) to get registered, sorry for the bad example. Once a contact person or number is added, an action (child postback or whatever) is called which validates the related model (not student model), adds it in a list and returns to the same page for further processing. Once all are added, the parent/master action validates whole student model and processes it.

How to validate the specific model for which an action is being called, add it to the page and return the same student view with added values in response?

Community
  • 1
  • 1
bjan
  • 2,000
  • 7
  • 32
  • 64

2 Answers2

1

This solution uses #2 (Session) since its simpler to code however this demonstrates the principles.

Views

Index View:

@using StackOverflow.Models

    <div>

    @{ Html.RenderPartial("PersonGrid", Model.Persons, new ViewDataDictionary()); }
    @Html.Partial("NewPerson", new Person())
    @{ Html.RenderPartial("ContactGrid", Model.Contacts, new ViewDataDictionary()); }
    @Html.Partial("NewContact", new Contact())

    @using(Html.BeginForm("Validate", "Home", FormMethod.Post))
    {
        <input type="submit" value="Validate" />
    }
    </div>

Person Grid


    @model IList

    <table>
        <thead>
            <tr>
                <td>First Name</td>
                <td>Last Name</td>
            </tr>
        </thead>

        <tbody>
            @if (Model != null && Model.Any())
            {
                foreach (var person in Model)
                {
                    <tr>
                        <td>@person.FirstName</td>
                        <td>@person.LastName</td>
                    </tr>
                }
            }
            else
            {
                <tr>
                    <td colspan="2" style="text-align: center">No persons available</td>
                </tr>
            }
        </tbody>
    </table>

Contact Grid


    @model IList

    <table>
        <thead>
            <tr>
                <td>Phone</td>
            </tr>
        </thead>
        <tbody>
            @if (Model != null && Model.Any())
            {
                foreach (var contact in Model)
                {
                    <tr>
                        <td>@contact.Phone</td>
                    </tr>
                }
            }
            else
            {
                <tr>
                    <td>No contacts available</td>
                </tr>
            }
        </tbody>
    </table>

New Person


    @model StackOverflow.Models.Person


    @using (Html.BeginForm("NewPerson", "Home", FormMethod.Post))
    {
        <div>
            @Html.Hidden("PersonViewState", TempData["PersonViewState"])

            @Html.LabelFor(m => m.FirstName)<br />
            @Html.TextBoxFor(m => m.FirstName)<br />

            <br />

            @Html.LabelFor(m => m.LastName)<br />
            @Html.TextBoxFor(m => m.LastName)<br />

            <br />
            <input type="submit" value="Submit" />
        </div>
    }

New Contact


    @model StackOverflow.Models.Contact

    @using (Html.BeginForm("NewContact", "Home", FormMethod.Post))
    {
        <div>
            @Html.LabelFor(m => m.Phone)<br />
            @Html.TextBoxFor(m => m.Phone)<br />
            <br />
            <input type="submit" value="Submit" />
        </div>
    }

Models



    public class Person
    {
        [Display(Name = "First Name")]
        public string FirstName { get; set; }

        [Display(Name = "Last Name")]
        public string LastName { get; set; }
    }

    public class Contact
    {
        [Display(Name = "Phone")]
        public string Phone { get; set; }
    }

    public class HomeModel
    {
        public IList<Person> Persons { get; set; }
        public IList<Contact> Contacts { get; set; }
    }

Helpers



    public static class PersistenceMechanism
    {
        public static IList GetPersons()
        {
            return (IList<Person>) HttpContext.Current.Session["__Persons"];
        }

        public static IList GetContacts()
        {
            return (IList<Contact>) HttpContext.Current.Session["__Contacts"];
        }

        public static void Update(IList<Person> persons)
        {
            HttpContext.Current.Session["__Persons"] = persons;
        }

        public static void Update(IList<Contact> contacts)
        {
            HttpContext.Current.Session["__Contacts"] = contacts;
        }
    }

Controller



    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new HomeModel
                            {
                                Persons = PersistenceMechanism.GetPersons(),
                                Contacts = PersistenceMechanism.GetContacts()
                            };
            return View(model);
        }

        [HttpGet]
        public ActionResult PersonGrid()
        {
            var persons = PersistenceMechanism.GetPersons();

            return PartialView(persons);
        }

        [HttpGet]
        public ActionResult ContactGrid()
        {
            var contacts = PersistenceMechanism.GetContacts();

            return PartialView(contacts);
        }

        [HttpPost]
        public ActionResult NewPerson(Person model)
        {
            var persons = PersistenceMechanism.GetPersons() ?? new List<Person>();
            persons.Add(model);
            PersistenceMechanism.Update(persons);

            return RedirectToAction("Index");
        }

        [HttpPost]
        public ActionResult NewContact(Contact model)
        {
            var contacts = PersistenceMechanism.GetContacts() ?? new List<Contact>();
            contacts.Add(model);
            PersistenceMechanism.Update(contacts);
            return RedirectToAction("Index");
        }

        [HttpPost]
        public ActionResult Validate()
        {
            var persons = PersistenceMechanism.GetPersons();
            var contacts = PersistenceMechanism.GetContacts();

            // validate
            // ...

            return RedirectToAction("Index");
        }
    }

  • Boy it took me a long time to post this. SO just kept complaining about indentation. –  Mar 01 '12 at 16:04
  • Great. Two things i want to ask 1- Using IList without type parameter, I don't think it is valid 2- HttpContext.Current is not going to work as per my knowledge, Sessions are Controller based. – bjan Mar 02 '12 at 05:34
  • I think its because I of the HTML encoding performed by SO when I was posting. I will update the post to reflect the generic parameter. –  Mar 02 '12 at 11:54
  • With this approach the Session is created by the time you are ready to use it. –  Mar 02 '12 at 11:55
  • One more issue, if the user opens up two pages/tabs with same view then the persons and contacts of one page will be overwritten by the other page. How to handle this situation? – bjan Mar 07 '12 at 10:09
  • A different persistence mechanism is required in this case. Our of the three the more suitable for your scenario is the persistence of the data within the page/form itself. Much the same as the ViewState mechanism that is provided with webforms. –  Mar 07 '12 at 11:19
0

To repeat the question to ensure that I have the idea of what you are asking.

Your page page is build from two partial views with different models. Both of these partial views contain a form which will on submit build a grid of records on the UI. The main page will have a further validate button which will then validate the entire contents of both grids on postback?

In this situation I will like to have two forms where the submit event is powered by Ajax. Either jQuery / Microsoft Ajax. Both forms will submit to two separate actions which will accept their respective models, Person and Contact. Each submit button will return its respective partial view which will be a grid showing the collection of items that have been submitted so far. The partial view returned will update a specified target (e.g. div) since we are using AJAX.

Of course it will be necessary to remember the previous items that were submitted so far in order reconstruct the grid with the new item added. This will mean some soft of persistent storage is required. Some options available are:

  1. Database
  2. Session
  3. Hidden form field(s) (preferred). It is possible to use the array model binding mechanism to support this or a simple serialized object (or list of objects) in a single hidden field.

Validation now becomes simple as the model are available server side through your persistent storage mechanism on postback.

  • What if the website has to run in a javascript-disabled environment ? – Shyju Feb 29 '12 at 15:47
  • This mechanism still works without the ajax. Just not a smooth UI experience. Because of the persistent storage mechanism then you can always rebuild the grids server side whether its a full postback or not. –  Feb 29 '12 at 15:54
  • @DaTribe I am not saying i would not use AJAX but what about pure ASP.NET MVC as it is not dependent on AJAX after all. I would appreciate sample code, it will be helpful without grid as well – bjan Mar 01 '12 at 05:10
  • I'm working on a proof of concept. Will update my answer shortly. –  Mar 01 '12 at 11:01