Recently, on one of the projects I had to do the edit form on ASP.NET MVC 3 so that saving data should happen asynchronously without reloading the page. To do this in ASP.NET MVC 3, there exists a function Ajax.BeginForm, which is behind the scenes uses a library jquery.unobtrusive-ajax.js
A good post on this topic before on this blog. But there is editing in a modal window. I decided to show an example of editing the entries from the list on the same page, but without postback.
For cosmetic purposes, I use a jQuery plugin that allows you to use a busy indicator in the container form.
So let us have a model:
public class PersonModel { [Key] public int Id { get; set; } [Required(ErrorMessage = "Required field")] [StringLength(10, MinimumLength = 3)] public string Name { get; set; } [Required] public string Email { get; set; } public int Age { get; set; } public IEnumerable<PersonModel> Persons { get; set; } }
Let`s create partial view EditPerson for asynchronous editing each person object. Let`s use Ajax.BeginForm for that:
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <h2>Edit Person @Model.Name</h2> @using (Ajax.BeginForm("EditPerson", new AjaxOptions { OnSuccess = "onSuccess", OnBegin = "onBegin", OnFailure = "handleError" })) { @Html.HiddenFor(model => model.Id) <div class="editor-label">Name</div> <div class="editor-field">@Html.EditorFor(model => model.Name)</div> <div class="editor-label">Email</div> <div class="editor-field">@Html.EditorFor(model => model.Email)</div> <div class="editor-label">Age</div> <div class="editor-field">@Html.EditorFor(model => model.Age)</div> <br/><hr/><br/> <input type="submit" value="Save" /> }
Of course, in order to intercept Ajax Submitting to controller, our action needs [HttpPost] attribute. In order to simply load up this form (for example, in some div) we need action without attribute also:
public class HomeController : Controller { ... public ActionResult EditPerson(int id) { Thread.Sleep(1000); return View(this.GetPersons().FirstOrDefault(x => x.Id == id)); } [HttpPost] public ActionResult EditPerson(PersonModel personModel) { if (ModelState.IsValid) { try { Thread.Sleep(1000); // todo: save person } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return this.Content(ex.Message); } } return View("SaveComplete", personModel); } ... }
In order to submitting occurred asynchronously, you must use the library jquery.unobtrusive-ajax, we plugging above. Submit ready.
Now let`s make the view that will be display a list of persons. When you click on the Edit button next to each entry, form for particular person will be loaded into container div. View, which displays a list of persons looks like this:
@model test.Models.PersonModel @{ ViewBag.Title = "Persons"; } <h2>Index</h2> <div> <table> @foreach (var item in @Model.Persons) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) @Html.ValidationMessageFor(modelItem => modelItem.Name) </td> <td> @Html.DisplayFor(modelItem => item.Email) @Html.ValidationMessageFor(modelItem => modelItem.Email) </td> <td> @Html.DisplayFor(modelItem => item.Age) </td> <td> <button onclick="@string.Format("editPerson({0})", item.Id)">Edit</button> </td> </tr> } </table> </div> <hr /> <div id="container"> </div>
and method for HomeController which returns a list of persons:
public class HomeController : Controller { ...
private IEnumerable<PersonModel> GetPersons() { return new List<PersonModel> { new PersonModel {Id = 1, Age = 21, Email = "1@mail.com", Name = "Person 1"}, new PersonModel {Id = 2, Age = 22, Email = "2@mail.com", Name = "Person 2"}, new PersonModel {Id = 3, Age = 23, Email = "3@mail.com", Name = "Person 3"} }; }
...}
Another view which we call SaveComplete, will show the successful preservation of person:
@model test.Models.PersonModel @{ ViewBag.Title = "SaveComplete"; Layout = null; } <h2>@Model.Name successfully saved</h2>
Finally, in order to make it work, you must define handlers on javascript, which will load our form editing when you click on the button next to the record. It is also necessary to define handlers onSuccess, onBegin and handleError, which are indicated on EditPerson view for error handling and display busy indicator. Let`s do it all on the Index view:
<script src="@Url.Content("~/Scripts/jquery.busy.min.js")" type="text/javascript"></script> <script type="text/javascript"> function editPerson(personId) { $('#container').busy(); if (personId == null) { return; } var editPersonUrl = '@Url.Action("EditPerson", new { id = "PersonIdPlaceholder", isNew = false })'; var editPersonUrlWithNotatId = editPersonUrl.replace('PersonIdPlaceholder', personId); editPersonUrlWithNotatId = editPersonUrlWithNotatId.replace(/&/g, '&'); $('#container').load(editPersonUrlWithNotatId, function () { $('#container').busy("hide"); }); } function onBegin() { $('#container').busy("busy"); } function onSuccess(context) { $('#container').html(context); $('#container').busy("hide"); } function handleError(response) { $('#container').html("Save error:" + response.responseText + "<br>Status code: " + response.status); $('#container').busy("hide"); } </script>
Link to full solution source code
Комментариев нет:
Отправить комментарий