开发者

ASP.NET MVC Add Child Records Dynamically

Using the Entity Data Framework, I have a model defined called Client which simply contains an id and a name. I then have an association with another model called Appointment containing an id and a date.

For the form, I wish to allow users to create a new client and on the same form, add appointments (before the client is created). I have it partially working (adding one appointment).

In the controller:

[HttpPost]
public ActionResult Create(Client client)
{
    var appointment = new Appointment();
    UpdateModel(appointment);
    client.Appointments.Add(appointment);
    db.AddToClients(client);
    db.SaveChanges();
    return RedirectToAction("Index");
}

In the view:

<div class="editor-label">
    <%= Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
    <%= Html.TextBoxFor(model => model.Name) %>
    <%= Html.ValidationMessageFor(model => model.Name) %>
</div>
<% Html.RenderPartial("Appointment", new Appointment()); %>

'Partial' view:

<div class="editor-label">
    <%= Html.LabelFor(model => model.AppointmentDate) %>
</div>
<div class="editor-field">
    <%= Html.TextBoxFor(model => model.AppointmentDate) %>
    <%= Html.ValidationMessageFor(model => model.AppointmentDate) %>
</div>

However, this is not ideal for several reasons:

  1. Only one appointment can be added
  2. Issues will occur if there are any name conflicts (e.g. a field called Comments in both Client and Appointment table), since no prefixing is done on the field names, which also prevents more than one appointment being added as well

What can I do to allow multiple appointments to be added, i.e. using a button to render a partial view (using AJAX probably) on the page when 'new appointment' is clicked to allow as many appointments as possible? I would also like this to be a shared view that can be used for both creating and editing records as well as adding/deleting appointments in the same form.

Update

Some progress, using a ViewModel:

public class ClientViewModel
{
    public Client Client { get; set; }
    public List<Appointment> Appointments { get; set; }
}

Controller:

public ActionResult Create()
{
    Client c = new Client();
    List<Appointment> appointments = new List<Appointment>();
    appointments.Add(new Appointment() { AppointmentDate = DateTime.Now });
    appointments.Add(new Appointment() { AppointmentDate = DateTime.Now.AddMonths(1) });

    var model = new ClientViewModel
    {
        Client = c,
        Appointments = appointments
    };
    return View(model);
}

[HttpPost]
public ActionResult Create(ClientViewModel m)
{
    try
    {
        foreach (var appointment in m.Appointments)
        {
            m.Client.Appointments.Add(appointment);
        }
        db.AddToClients(m.Client);

        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

View (for appointments, does not work with a foreach loop, as each of the appointments are prefixed with Appointment_ rather than Appointment[0]_):

<%= Html.EditorFor(model => Model.Client) %>
<% for (int i = 0; i < Model.Appointments.Count(); i++) { %>
    <%= 开发者_StackOverflowHtml.EditorFor(model => Model.Appointments[i])%>
<% } %>

This is fine for creating appointments for a new client, but what about editing them, as well as adding new appointments without posting back the form? For adding, a link that shows a new appointment form under the existing one, with another link to delete (hide from view and set a hidden field value).

Update 2

Got it working, for adding appointments when first creating a client. However, I am not sure how to update existing appointments (without just deleting them all and adding again).

In controller:

[HttpPost]
public PartialViewResult AddAppointment(Appointment appointment, int index)
{
    List<Appointment> appointments = new List<Appointment>();
    for (int i = 0; i < index; i++)
    {
        appointments.Add(null);
    }
    appointments.Add(new Appointment() { AppointmentDate = DateTime.Now });
    ViewData["index"] = index;
    var model = new ClientViewModel
    {
        Appointments = appointments
    };
    return PartialView("Appointment", model);
}

View:

<%= Html.EditorFor(model => Model.Client) %>
<div id="appointments">
<% for (int i = 0; i < Model.Appointments.Count(); i++) { %>
    <%= Html.EditorFor(model => Model.Appointments[i]) %>
<% } %>
</div>
<a href="javascript:;" id="addappointment">Add Appointment</a>
<script type="text/javascript">
    var appIndex = <%= Model.Appointments.Count() %>;
    $("#addappointment").click(function () {
        $.post(
            "/Client/AddAppointment",
            {"index" : appIndex}, 
            function (result) {
                $("#appointments").append(result);
                // increase index by 1
                appIndex++;
            }
        );
});


Just floating some ideas around..

How about if you make the Appointment partial view contain a form.

Use jQuery/AJAX to handle the submission of the form:

$.post(
   "/SomeController/AddAppointment",
        appointment, // grab this from the form
        function (result) { 
           $("#target").html(result); // this will be the div which contains the partial
        }
     );

Which would send the strongly-typed Appointment to the following Action method:

[HttpPost]
public PartialViewResult AddAppointment(Appointment appointment)
{
   // code to persist entity
   // code to add errors to model (if necessary)
   return PartialView("Appointment");
}

Basically, your submitting the form using AJAX, then re-rendering the result back into the partial. The result is similar to the AJAX UpdatePanel (of WebForms fame).

So you'd fill out the details, hit "submit", the details gets posted to the controller (using AJAX), then the form would be blank again, so you can keep adding appointments.

Would that work?


There are couple of blog posts that explains how model binding works with a list of complex item. Your view model approach should work, when ever you want to add a new appointment just add a new item to the list of appointments (viewmodel) on client side with some JS code.

Go through these links which explains how the out of box MVC model binding would work if you render the html correctly

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜