Question

Let's say I have a very huge model that contains lists and even those lists can have objects that contain other lists. I want to create an edit form for that in MVC4 (or 5) without AJAX.

So I figured that the first part of that is to store the entire object on the client side in hidden fields. List binding works like a charm, see http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/. Now the complete roundtrip is working, I can push out the entire object to the client in bound hidden fields, it gets back to me upon submit and the posted hidden fields are put into the complex object, nested lists and everything included.

Lists or other objects should become editable based on some action. One scenario is where a single object or list items are displayed as non-editable, and when the user clicks it, it becomes editable in-place, so for example the cells in a grid become textboxes. Another scenario is where a single object or list items are not shown at all, and when the user clicks a button, a popup window appears with the text input fields.

Is there a library or a proven way to do this?

Was it helpful?

Solution 3

The very huge model is a business requirement, it has to be edited as one entity, preferably on one page, it makes perfect sense but I can't talk about it. I originally thought (or hoped) there was an easy to describe solution, but apparently, this isn't a common thing in MVC. It would be very different with AJAX, it has its pros and cons. For sure it's more widely used, hence more documented. Without AJAX, there is only one round-trip, which is a bit bigger, but it's a smoother user experience. Anyway, here's a rough guide how to do it the way I asked.

The client-server roundtrip is handled by MVC with (mostly) hidden fields as I said in the question. Later it can be optimized by encoding some stuff in JSON instead of hidden fields, it doesn't affect the rest of the system.

Normal fields are stored in normal editors, not hidden fields. It makes no difference from the perspective of the client-server roundtrip. So these can be edited in place.

Grid rendering is also easy. Server-side MVC grids are suboptimal in this case, because they would send redundant data to the client side. Luckily there are a lot more client-side grid solutions, they are by nature server platform independent. Just collect the required data from the hidden fields and use a JavaScript grid library to build a grid from it when the page loads. Naturally, as I said, the lists can contain lots of data and other nested lists, but in this simple grid, a few necessary columns must be selected, no problem with that.

Now comes the interesting part, how to edit a grid row with all that complex data. Let's say I have a list of Persons in my model, and they have a list of Addresses. There is a Person grid, and when you click on a row, you want the end user to be able to edit additional data for a Person and also his Addresses.

First of all, the Person editor template has to be sent to the client side in advance. We need to put that editor template inside our view, and hide it. Whenever the user wants to edit a person, we create a JS dialog with the contents of that editor template.

We need to bind the Person editor template to a Person object stored in the hidden fields. Based on which row the user clicked, we get an index, and we bind Model.Persons[index] to that template. Knockout.js is a good candidate for JS binding. It does all the field copying back and forth.

An MVC editor template can also contain validation logic. It's no problem in this case, because we rendered the editor template to the client side as a whole. The validation will happen inside the popup window without any kind of magic. When the user presses the save button, validation will run, and when it succeeds, we use the binding engine to copy the popup contents back into the hidden fields.

It is not the simplest of things, but it is very straightforward. Actually, several different JS libraries are needed, not as I hoped. So if anyone wants to edit a complex model on a single page without AJAX, it is certainly possible. It's still not completely documented because I can't share more details. But it has its advantages: only one round-trip, hence faster user experience, and there's no need to maintain state on the server, all the data is retrieved and saved as one entity in one roundtrip.

OTHER TIPS

Is there a library or a proven way to do this?

Exactly what for? Displaying a massive model in view for editing? Making a grid editable? Or popping up the records of a grid for editing?

Basically, I think you are overcomplicating this matter a bit. If you apply some separation of concerns here you will find out how easy everything becomes and in fact it's super easy to implement and most importantly much easier to maintain and scale.

Starting from the model, let's assume you've got this all-mighty massive model named Company with the following properties:

  • CompanyID (int)
  • CompanyName (string)
  • CompanyLegalID (string)
  • CompanyRegistrationNumber (string)
  • ContactInfo (ContactInfo class)
  • HeadQuaterAddress ('Address` class)
  • Branches (List of Branch classes)
  • Employees (List of Employee classes)

And the list of properties could go on and on forever.It would be easier, super easier to break down this model into smaller models. Make a CompanyModel model with the following properties...

  • CompanyID (int)
  • CompanyName (string)
  • CompanyLegalID (string)
  • CompanyRegistrationNumber (string)

Then make a CompanyContactInfo model and so on. Getting the idea? Again, separation of concerns simplifies matters a lot. Then, create action methods and views to read/edit these models.

Now for lists properties of the massive object you'd like to do the same. For example, for the list of Employee objects it'd be easier to create a CompanyEmployeesModel model with the following properties:

  • CompanyId (int)
  • Employees (List of EmployeeModel classes)

Then create a controller action method to show the list of employees...

public ActionResult EmployeeList(int companyId)
{
    var employees = BusinessLogic.Get_Me_Employees_For(companyId);
    CompanyEmployeesModel model = new CompanyEmployeesModel();
    model.CompanyId = companyId;
    model.Employees = employees;

    return View(model);
}

Hope you are getting the idea so far. In the view simply create a css-formatted table to show the employee list, razor makes it super simple...

<table class="grid">
   <tr>
      <th></th>
        <th>
            Name
         </th>
         <th>
            Phone
          </th>
    </tr>

            @{
                 var alt = false;

                foreach (var emp in Model.Employees)
                {                    
                    <tr class="@(alt ? "alt" : string.Empty)">
                        <td class="cmd">
                            @ActionLink("edit", "Edit", "Employees", new { empId = emp.Id}, null)
                        </td>
                        <td>@emp.Name</td>
                        <td>@emp.Phone</td>
                    </tr>

                    alt = !alt;
                }

            }
</table>

Notice that the first column of the table has link "edit" that takes the user to the Edit action method of the Employee controller where obviously you will do exactly the same you've been doing with smaller models. All I'm doing is separating the logic, models, view and making them simpler, easier to maintain and easier to understand.

Hope it all makes sense to you

Jeditable http://www.appelsiini.net/projects/jeditable which does the same thing and is easier to implement

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top