MVC 4 Editar formulario modal usando Bootstrap

Estoy usando MVC 4 y Entity Framework para desarrollar una aplicación web de intranet. Tengo una lista de personas que se pueden modificar mediante una acción de edición. Quería que mi aplicación fuera más dinámica mediante el uso de formularios modales. Así que traté de poner mi vista de edición en mi modal Bootstrap y tengo 2 preguntas al respecto:

  • ¿Debo usar una vista simple o parcial?
  • ¿Cómo puedo realizar la validación? (En realidad funciona, pero me redirecciona a mi vista original, por lo tanto no en la forma modal)

Creo que tengo que usar AJAX y / o jQuery, pero soy nuevo en estas tecnologías. Cualquier ayuda sería apreciada.

EDITAR: Mi vista de índice:

@model IEnumerable @{ ViewBag.Title = "Index"; } 

Index


@using (Html.BeginForm("SelectedPersonDetails", "Person")) { } @foreach (BuSIMaterial.Models.Person item in ViewBag.PageOfPersons) { }
Firstname Lastname Start Date End Date Details Actions
@item.FirstName @item.LastName @item.StartDate.ToShortDateString() @if (item.EndDate.HasValue) { @item.EndDate.Value.ToShortDateString() } Details
National Number @item.NumNat
Vehicle Category @item.ProductPackageCategory.Name
Upgrade@item.Upgrade
House to work @item.HouseToWorkKilometers.ToString("G29")
@section Scripts { @Scripts.Render("~/bundles/jqueryui") @Styles.Render("~/Content/themes/base/css") $(document).ready(function () { $('#tbPerson').autocomplete({ source: '@Url.Action("AutoComplete")' }); $(".details_link").click(function () { var id = $(this).data("target-id"); var url = '/ProductAllocation/ListByOwner/' + id; $("#details_"+ id).load(url); }); $('.edit-person').click(function () { var url = "/Person/EditPerson"; var id = $(this).attr('data-id'); $.get(url + '/' + id, function (data) { $('#edit-person-container').html(data); $('.edit-person').modal('show'); }); }); }); }

Mi vista parcial:

 @model BuSIMaterial.Models.Person  
@using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post, new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "list-of-people" })) { @Html.ValidationSummary() @Html.AntiForgeryToken() }

Deberías usar vistas parciales. Yo uso el siguiente enfoque:

Use un modelo de vista para no pasar sus modelos de dominio a sus vistas:

 public class EditPersonViewModel { public int Id { get; set; } // this is only used to retrieve record from Db public string Name { get; set; } public string Age { get; set; } } 

En su PersonController:

 [HttpGet] // this action result returns the partial containing the modal public ActionResult EditPerson(int id) { var viewModel = new EditPersonViewModel(); viewModel.Id = id; return PartialView("_EditPersonPartial", viewModel); } [HttpPost] // this action takes the viewModel from the modal public ActionResult EditPerson(EditPersonViewModel viewModel) { if (ModelState.IsValid) { var toUpdate = personRepo.Find(viewModel.Id); toUpdate.Name = viewModel.Name; toUpdate.Age = viewModel.Age; personRepo.InsertOrUpdate(toUpdate); personRepo.Save(); return View("Index"); } } 

Luego crea una vista parcial llamada _EditPersonPartial . Esto contiene el encabezado modal, cuerpo y pie de página. También contiene la forma Ajax. Está fuertemente tipado y toma en nuestro modelo de vista.

 @model Namespace.ViewModels.EditPersonViewModel  
@using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post, new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "list-of-people" })) { @Html.ValidationSummary() @Html.AntiForgeryToken() }

Ahora, en algún lugar de su aplicación, diga otro _peoplePartial.cshtml parcial, etc.

 
@foreach(var person in Model.People) { }
// this is the modal definition

Prefiero evitar el uso de Ajax.BeginForm helper y hacer una llamada Ajax con JQuery. En mi experiencia, es más fácil mantener un código escrito así. A continuación se detallan los detalles:

Modelos

 public class ManagePeopleModel { public List People { get; set; } ... any other properties } public class PersonModel { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } ... any other properties } 

Vista de padres

Esta vista contiene las siguientes cosas:

  • registros de personas para iterar a través de
  • un div vacío que se completará con un modal cuando una persona necesita ser editada
  • un poco de JavaScript manejando todas las llamadas ajax
 @model ManagePeopleModel 

Manage People

@using(var table = Html.Bootstrap().Begin(new Table())) { foreach(var person in Model.People) { @person.Id @Person.Name @person.Age @html.Bootstrap().Button().Text("Edit Person").Data(new { @id = person.Id }).Class("btn-trigger-modal") } } @using (var m = Html.Bootstrap().Begin(new Modal().Id("modal-person"))) { } @section Scripts { }

Vista parcial

Esta vista contiene un modal que se completará con información sobre la persona.

 @model PersonModel @{ // get modal helper var modal = Html.Bootstrap().Misc().GetBuilderFor(new Modal()); } @modal.Header("Edit Person") @using (var f = Html.Bootstrap.Begin(new Form())) { using (modal.BeginBody()) { @Html.HiddenFor(x => x.Id) @f.ControlGroup().TextBoxFor(x => x.Name) @f.ControlGroup().TextBoxFor(x => x.Age) } using (modal.BeginFooter()) { // if needed, add here @Html.Bootstrap().ValidationSummary() @:@Html.Bootstrap().Button().Text("Save").Id("btn-person-submit") @Html.Bootstrap().Button().Text("Close").Data(new { dismiss = "modal" }) } } 

Acciones del controlador

 public ActionResult GetPersonInfo(int id) { var model = db.GetPerson(id); // get your person however you need return PartialView("[Partial View Name]", model) } public ActionResult UpdatePersonInfo(PersonModel model) { if(ModelState.IsValid) { db.UpdatePerson(model); // update person however you need return Json(new { success = true }); } // else return PartialView("[Partial View Name]", model); } 

En respuesta a la respuesta de Dimitrys, pero usando Ajax.BeginForm los siguientes trabajos funcionan al menos con MVC> 5 (4 no probados).

  1. escribe un modelo como se muestra en las otras respuestas,

  2. En la “vista principal” probablemente usará una tabla para mostrar los datos. El modelo debería ser un ienumerable. Supongo que el modelo tiene una propiedad id . Sin embargom debajo de la plantilla, un marcador de posición para el javascript modal y correspondiente

      @foreach (var item in Model) {  } 
    @Html.Partial("dataRowView", item)

observe el “editor-id-éxito” en las filas de la tabla de datos.

  1. El dataRowView es un parcial que contiene la presentación del artículo de un modelo.

     @model ModelView @{ var item = Model; } 
    // some data
  2. Escriba la vista parcial que se invoca haciendo clic en el botón de la fila (a través de JS $('.editor-container').click(function () ... ).

     @model Model  @using (Ajax.BeginForm("MyEditAction", "Controller", FormMethod.Post, new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "editor-success-" + @Model.Id, OnSuccess = "success", OnFailure = "failure", })) { @Html.ValidationSummary() @Html.AntiForgeryToken() @Html.HiddenFor(model => model.Id)   } 

Aquí es donde sucede la magia: en AjaxOptions , UpdateTargetId reemplazará la fila de datos después de la edición, onfailure y onsuccess controlará el modal.

Esto es, el modal solo se cerrará cuando la edición sea exitosa y no haya habido ningún error, de lo contrario, el modal se mostrará después de la publicación ajax para mostrar los mensajes de error, por ejemplo, el resumen de validación.

Pero, ¿cómo obtener ajaxform para saber si hay un error? Esta es la parte del controlador, simplemente cambie response.statuscode como se indica a continuación en el paso 5:

  1. el método de acción del controlador correspondiente para el modal de edición parcial

     [HttpGet] public async Task EditPartData(Guid? id) { // Find the data row and return the edit form Model input = await db.Models.FindAsync(id); return PartialView("EditModel", input); } [HttpPost, ValidateAntiForgeryToken] public async Task MyEditAction([Bind(Include = "Id,Fields,...")] ModelView input) { if (TryValidateModel(input)) { // save changes, return new data row // status code is something in 200-range db.Entry(input).State = EntityState.Modified; await db.SaveChangesAsync(); return PartialView("dataRowView", (ModelView)input); } // set the "error status code" that will redisplay the modal Response.StatusCode = 400; // and return the edit form, that will be displayed as a // modal again - including the modelstate errors! return PartialView("EditModel", (Model)input); } 

De esta forma, si se produce un error al editar los datos del Modelo en una ventana modal, el error se mostrará en el modal con los métodos validationummary de MVC; pero si los cambios se realizaron correctamente, se mostrará la tabla de datos modificada y la ventana modal desaparecerá.

Nota: si obtienes unajaxoptions funcionando, debes indicarle a tu configuración de paquetes que vincule jquery.unobtrusive-ajax.js (puede ser instalado por NuGet):

  bundles.Add(new ScriptBundle("~/bundles/jqueryajax").Include( "~/Scripts/jquery.unobtrusive-ajax.js"));