Render PartialView within View on Demand - c#

the page has 2 DropDownLists + a Textbox + a Submit Button.
On Submit it's supposed to choose and fill a partial view depending on what value is selected in the first DropDownList. I got it somewhat working; however, it will always return the pv in a new window instead of rendering it in the main view's div.
I am using MVC 3 + Telerik.
The most Important parts of the code:
VIEW - Updated
<script type="text/javascript">
function onMainDDL1Change(e) {
var combo = $("#SubDDL1").data("tComboBox");
combo.value("");
combo.reload();
}
function onSubDDL1DataBinding(e) {
var combo = $("#MainDDL1").data("tComboBox");
e.data = $.extend({}, e.data, { mainDDL1ID: combo.value() });
}
</script>
#using (Ajax.BeginForm("PartialGrid", "DataSearch", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "result", InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace }))
{
<table>
<tr>
<td>
#(Html.Telerik().ComboBox()
.Name("MainDDL1")
.AutoFill(true)
.DataBinding(binding => binding.Ajax().Select("LoadMainDDL", "DataSearch"))
.HighlightFirstMatch(true)
.ClientEvents(events => events.OnChange("onMainDDL1Change"))
)
</td>
</tr>
<tr>
<td>
#(Html.Telerik().ComboBox()
.Name("SubDDL1")
.DataBinding(binding => binding.Ajax().Select("LoadSubDDL1", "DataSearch"))
.HighlightFirstMatch(true)
.ClientEvents(events => events.OnDataBinding("onSubDDL1DataBinding"))
)
</td>
<tr>
<td>
#Html.TextBoxFor(o => o.sSearch1)
</td>
</tr>
<tr align="center">
<td colspan="4">
<input type="submit" class="t-button" value="Search" name="submit" />
</td>
</tr>
</table>
}
<div id="result">
</div>
Controller - Updated
[HttpPost]
//PartialView
public PartialViewResult PartialGrid(DataSearchModel voModel)
{
voModel.dtResultSet1 = DLA.DataSearchContext.getResultSet1(voModel.MainDDL1, voModel.SubDDL1, voModel.sSearch1);
return PartialView("_TPRCL", voModel);
}
//Initial View
public ViewResult DataSearch(string text)
{
DataSearchModel oModel = new DataSearchModel();
oModel.alMainDDL = DLA.DataSearchContext.getMainDDL();
return View(oModel);
}
PartialView
#model ISC.Utilities.GISTransactionTools.Models.DataSearchModel
#(Html.Telerik().Grid(Model.dtResultSet1)
.Name("Grid")
.Footer(false)
.Columns(columns =>
{
columns.Bound(o => o.Row[0]).Title("T_PRCL");
}))
PartialView Grid has actually more columns, this is just to make it work.

I am not using Telerik, but here is how I am doing something similar:
In the initial view I have the following code:
#using (Ajax.BeginForm("PhoneForm", "Home", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "form-lead", InsertionMode = InsertionMode.Replace, OnSuccess = "HookupValidation" })) {
#Html.ValidationSummary(false, "Please address the following items:")
<div class="phone">
<div class="field">
#Html.LabelFor(model => model.Phone1)
#Html.EditorFor(model => model.Phone1)
#Html.ValidationMessageFor(model => model.Phone1)
</div>
<div class="button">
<input type="submit" value="Get Customer" name="submit" /></div>
</div>
}
<div id="form-lead">
</div>
Basically, when the "Get Customer" button is clicked, it will call, via AJAX, the "PhoneForm" method in my "Home" controller. This method generates the partial view which is then inserted (InsertionMode = InsertionMode.Replace) into the (UpdateTargetId = "form-lead"). The OnSuccess function is just to be sure that client side validation and jQuery events are hooked up on the partial view. If you don't do this, none of the client side validation or script will work for items in the partial view.
The controller code for "PhoneForm" is as follows:
[HttpPost]
public PartialViewResult PhoneForm(string Phone1) {
HomeViewModel viewModel = new HomeViewModel();
// ... get data and set up view model here ... //
// ... also choose which partial view you want to return ... //
return PartialView("LeadInfoPartial", viewModel);
}
Hope this helps you solve your issue.

Alright got it to work in Javascript.
View
<script type="text/javascript"> $('#btnSubmit').click(function () {
var time = new Date().getTime(); // #* unique random number to workaround IE cache issue - IE will cache the ajax if you
don't use this *#
var oMainDDL1 = $('#MainDDL1').data("tComboBox");
var oSubDDL1 = $('#SubDDL1').data("tComboBox");
var sSearch1 = $("#Search1").val();
var actionURL = '#Url.Action("getGrid1", "DataSearch", new { MainDDL1
= "PLACEHOLDER" })'.replace('PLACEHOLDER', oMainDDL1.value()) + "&SubDDL1=" + oSubDDL1.value() + "&Search1=" + sSearch1 + "&time=" +
time;
if (actionURL != null) {
$.get(actionURL, function (data) {
$('#result1').fadeOut('slow', 'linear', function () { $('#result1').empty(); $('#result1').append(data); });
$('#result1').fadeIn('slow', 'linear', function () {
if ($.browser.msie) {
this.style.removeAttribute('filter'); // #* Needed to fix IE7 cleartype bug with jQuery fade, but will crap out
on FF/Chrome *#
}
});
});
}
});
}); </script>
#(Html.Telerik().ComboBox()
.Name("MainDDL1")
.AutoFill(true)
.DataBinding(binding => binding.Ajax().Select("LoadMainDDL", "DataSearch"))
.HighlightFirstMatch(true)
.ClientEvents(events => events.OnChange("onMainDDL1Change"))
)
#(Html.Telerik().ComboBox()
.Name("SubDDL1")
.DataBinding(binding => binding.Ajax().Select("LoadSubDDL1", "DataSearch"))
.HighlightFirstMatch(true)
.ClientEvents(events => events.OnDataBinding("onSubDDL1DataBinding"))
)
#Html.TextBox("Search1")
<input type="button" class="t-button button1" value="Search"
id="btnSubmit" />
<div id="result1"> </div>
Controller
public PartialViewResult getGrid1(string MainDDL1, string SubDDL1, string Search1)
{
DataSearchModel voModel = new DataSearchModel();
voModel.dtResultSet1 = DLA.DataSearchContext.getResultSet1(MainDDL1, SubDDL1, Search1);
return PartialView(MainDDL1, voModel);
}
public ViewResult DataSearch(string text)
{
DataSearchModel oModel = new DataSearchModel();
oModel.alMainDDL = DLA.DataSearchContext.getMainDDL();
return View(oModel);
}

Related

How do you change view by changing dropdownlist?

I have a dropdown list in a View. And if I change the selection, I want to change immediately the values in the View. The textbox of Type and Description.
How do you do that?
#Model GenreModel
#using (Html.BeginForm("EditGenre", "Home", FormMethod.Post))
{
#model GenreModel
<div class="text-center">
<h1 class="display-4">Edit genre</h1>
<p> <input type="submit" value="submit" name="Save" /></p>
<p>
<label>Select genre: </label>
#Html.DropDownListFor(Model => Model.Description, new SelectList(ViewBag.GenreList, "Description", "Description"), new { onchange = "onChangeSelection(this.value);" });
</p>
<p>
<label>Type</label>#Html.TextBoxFor(Model => Model.Type)
</p>
<p>
<label>Description</label>#Html.TextBoxFor(Model => Model.Description)
</p>
</div>
}
This is the method in the Controller.
[HttpPost]
public IActionResult EditGenre(string description)
{
var dto = _genreService.GetGenreByName(description);
var model = _mapper.Map<GenreDto, GenreModel>(dto);
ViewBag.GenreList = _genreService.GetAllGenres().OrderBy(g => g.Description);
return View(model);
}
and finally:
<script>
function onChangeSelection(val) {
{
$.ajax({
url: #Url.Action("EditGenre", "Home"),
//type: "POST",
data: { description: val },
success: function (data) {
// put result of action into element with class "result"
$('.result').html(data);
},
error: function () {
alert(val + ' not found.');
}});
}
</script>
Change the DropDownList like this:
#Html.DropDownListFor(Model => Model.Description, new SelectList(ViewBag.GenreList, "Description", "Description"));
And submit the form in the onchange function:
<script>
$("#Description").on("change", function () {
$(this).closest("form").submit();
})
</script>
It seems my DropDownList didn't fire a Submit. I leave the javascript and using a hidden field.
And this solution works fine.
<p>
<label>Select genre: </label>
#Html.Hidden("#Discription")
#Html.DropDownListFor(Model => Model.Description, new SelectList(ViewBag.GenreList, "Description", "Description"), new { onchange = "this.form.submit();" });
</p>

How to use ajax in pagedList

I am using paged list and calling ajax when i click on next that is working.The problem is when i click on previous or page number that does not call ajax.I need to use ajax call in paged List rather than my own ajax.
public ActionResult ApplicantsRecord(int? page)
{
List<ApplicantsRecord> ar = new List<ApplicantsRecord>();
ApplicantsRecord a = new ApplicantsRecord();
List<ApplicantsRecordDetailViewModel> apvmlist = new List<ApplicantsRecordDetailViewModel>();
ApplicantsRecordDetailViewModel apvm = new ApplicantsRecordDetailViewModel();
//ar = db.ApplicantsRecords.ToList();
var groupedAR = db.ApplicantsRecords.GroupBy(x => x.SessionId)
.Select(y => new
{
SessionId = y.Key,
ApplicationsRecords = y.FirstOrDefault(),
}).ToList().OrderByDescending(x => x.ApplicationsRecords.LoginDate);
foreach (var i in groupedAR)
{
ar.Add(i.ApplicationsRecords);
}
int pageNumber = (page ?? 1);
if(Request.IsAjaxRequest())
{
return PartialView("_ApplicantsRecord", ar.ToPagedList(pageNumber, 10));
}
return View(ar.ToPagedList(pageNumber, 10));
}
code of view
<div id="targetContainer">
#Html.Partial("_ApplicantsRecord",Model);
</div>
code for ajax
var npage =2;
$(document).ready(function () {
$('#container').click(function () {
$.ajax({
type: "GET",
traditional: true,
cache: false,
url: '/Testing/ApplicantsRecord/',
data:{page:npage}
})
.success(function (html) {
UpdatePage(html);
})
.error(function () {
});
return false;
});
});
function UpdatePage(html) {
$("#targetContainer").empty().html(html);
newpage = $('#newpage').val();
npage = parseInt(npage)
npage = npage + 1;
$('#newpage').val(npage);
}
and here is the partial view
i got it i was not using jquery unobtrusive in my jquery bundle
i downlloaded jquery unobtrusive ui using Nuget package and added in a new bundle
then include that bundle in my _Layout view instead of jquery bundle it started work
and changed the patrial view code to this one
#model IPagedList<ApplicantsRecord>
<div id="container">
<div class="pagedList">
#Html.PagedListPager(Model, page => Url.Action("ApplicantsRecord", new { page = page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions()
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "targetContainer"
}))
</div>
</div>
<table>
<thead>
<tr>
<th width="200" align="center">User Name</th>
<th width="200" align="center">Login Date Time</th>
<th width="100" align="center">Details</th>
</tr>
</thead>
<tbody>
#foreach (var group in Model.GroupBy(x => x.UserName))
{
<tr class="group-header">
<td colspan="6">
<span class="label label-info">Applicant : #group.Key</span>
<span class="label label-success">Total Test Taken: #group.Count()</span>
</td>
</tr>
foreach (var item in group)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserName)</td>
<td>#Html.DisplayFor(modelItem => item.LoginDate)</td>
<td>#Html.ActionLink("Details", "ApplicantsRecordDetail", new { id = item.SessionId })</td>
</tr>
}
}
</table>
and the code of View to this one
#model IPagedList<ApplicantsRecord>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "ApplicantsRecord";
}
<h2>ApplicantsRecord</h2>
<p>
#Html.ActionLink("Back To List", "QuestionDetail") |
#Html.ActionLink("Live View ", "LiveView")
</p>
<div id="targetContainer">
#Html.Partial("_ApplicantsRecord",Model)
</div>

Refresh partial view content when submit button clicked

I have a strongly typed view, which has a form in which I put one field showtime when the submit button is clicked I want to save the entry into database also display that entry along with other entries on the database inside the partial view I put on the main view . When the Index action method is called the Main view is rendered .I want to update the Partial view when a new entry is saved without reloading the whole page ,just refresh the partial page.
Here is my view :
#model Bookmany.Admin.Models.Theater.TheaterShowTimeViewModel
#{
ViewBag.Title = "Theater showTimes / BookMany";
}
<h3>Theater ShowTimes</h3>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.TheaterID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TheaterID, Model.AvailableTheaters)
#Html.ValidationMessageFor(model => model.TheaterID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ShowTimeName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ShowTimeName)
#Html.ValidationMessageFor(model => model.ShowTimeName)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.Partial("_TheaterShowTimeList", Model.TheaterShowTimeList)
</div>
Here is my partial view :
#model IEnumerable<Bookmany.Core.Domain.TheaterShowTime>
<table class="table">
<tr>
<th>
Theater Name
</th>
<th>
#Html.DisplayNameFor(model => model.ShowTimeName)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Theater.TheaterName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.TheaterShowTimeID }) |
#Html.ActionLink("Details", "Details", new { id = item.TheaterShowTimeID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.TheaterShowTimeID })
</td>
</tr>
}
My action method :
public ActionResult Index()
{
var model = new TheaterShowTimeViewModel();
//Bind Theater dropdown with selected value
model.AvailableTheaters = GetTheaters();
model.TheaterShowTimeList = _theaterShowTimeService.GetAllTheaterShowTime();
return View(model);
}
[HttpPost]
public ActionResult Index(TheaterShowTimeViewModel model)
{
if (ModelState.IsValid)
{
InsertOrUpdateTheaterShowTime(model);
model.TheaterShowTimeList=_theaterShowTimeService.GetAllTheaterShowTime();
return PartialView("_TheaterShowTimeList", model.TheaterShowTimeList);
}
return View(model);
}
my problems :
when I enter one value in the field and submit the form ,the entry saved now the partial view is only returned with the updated list
I want to update the partial view without reloading the whole page how do achieve that?
Like Stephen Muecke said I posted the form using ajax when the submit button clicked
<script type="text/javascript" >
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#ShowTimeName').val("");
$('#theaterShowList').html(result);
}
});
}
return false;
});
});
</script>
In my action method which returns the partial view :
[HttpPost]
public ActionResult Index(TheaterShowTimeViewModel model)
{
if (ModelState.IsValid)
{
InsertOrUpdateTheaterShowTime(model);
model.TheaterShowTimeList=_theaterShowTimeService.GetAllTheaterShowTime();
//return RedirectToAction("Index", new { id = model.TheaterID });
//return Json(model.TheaterShowTimeList);
return PartialView("_TheaterShowTimeList", model.TheaterShowTimeList);
}
return View(model);
}
this resolved my issue

ajax post to httpost action method with no refresh special load in

First off I must say I have a bit of a brain teaser here. I may be a bit new to ASP.Net MVC4 but I have never attempted anything like this. The end goal is a type of tree view for partial pages in a standard page. Right now I am trying to use ajax to load a partial in a div while passing the users selection to the next view(it will than be used to narrow that pages results).
<div>
<div>
<form id="myForm" action="ScheduleZone" method="post">
<table class="table">
<tr>
<th>Store
</th>
</tr>
#foreach (ACS.Models.Tree rate in TempML.Tree)
{
<tr>
<td>#Html.DisplayFor(model => rate.Store)#Html.ActionLink("+", " ScheduleZone", rate, new { #class = "g" })
</td>
</tr>
}
</table>
</form>
</div>
<div class="div" id="mainDiv">
</div>
<div class="div" id="Zone">
</div>
<div class="div" id="Sell">
</div>
<div class="div" id="Meh">
</div>
<div class="div" id="Sk">
</div>
</div>
<script type="text/javascript">
$(".g").click(function () {
$("#mainDiv").load("/Home/Zone");
});
</script>
I can't seem to call the page in the div after calling the http post action. It returns just the partial view not in the div.
public ActionResult Zone(CommisionsTree tree)
{
string banner = "2";
CommissionDAO dao = new CommissionDAO();
DataSet commissionInfo = dao.GetCommissionRates(tree, banner);
ModelList ml = new ModelList();
foreach (DataTable Table in commissionInfo.Tables)
{
foreach (DataRow row in Table.Rows)
{
Tree commTree = new Tree();
commTree.Banner = Convert.ToInt32(banner);
commTree.Store = tree.Store;
foreach (DataColumn column in Table.Columns)
{
switch (column.ColumnName)
{
case "SCHED_ZONE":
commTree.ScheduleZone = row[column].ToString();
break;
default:
break;
}
}
ml.CommissionTree.Add(commTree);
}
TempData["TempModelList"] = ml;
}
return PartialView(ml);
}
You can replace your link + javascript with an AjaxLink
#Ajax.ActionLink("+", "Zone", "Home", new AjaxOptions() { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "mainDiv" }, htmlAttributes: new { #class="someClasses", id = "anId"})
In this case, the reason why it's following link after it's loading it's because your need to return false at the end of your javascript.
<script type="text/javascript">
$(".g").click(function () {
$("#mainDiv").load("/Home/Zone");
return false;
});
</script>

MVC 4 Edit modal form using Bootstrap

I'm using MVC 4 and Entity Framework to develop an intranet web application. I have a list of persons which can be modify by an edit action. I wanted to make my app more dynamic by using modal forms. So I tried to put my edit view into my Bootstrap modal and I have 2 questions about it :
Should I use a simple or a partial view?
How can I perform the validation (actually it work but it redirects me to my original view so not in the modal form)
I think I have to use AJAX and/or jQuery but I'm new to these technologies. Any help would be appreciated.
EDIT : My Index View :
#model IEnumerable<BuSIMaterial.Models.Person>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<br />
<div class="group">
<input type="button" value="New person" class="btn" onclick="location.href='#Url.Action("Create")';return false;"/>
<input type="button" value="Download report" class="btn" onclick="location.href='#Url.Action("PersonReport")';return false;"/>
</div>
#using (Html.BeginForm("SelectedPersonDetails", "Person"))
{
<form class="form-search">
<input type="text" id="tbPerson" name="tbPerson" placeholder="Find an employee..." class="input-medium search-query">
<button type="submit" class="btn">Search</button>
</form>
}
<table class="table">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Start Date</th>
<th>End Date</th>
<th>Details</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
#foreach (BuSIMaterial.Models.Person item in ViewBag.PageOfPersons)
{
<tr>
<td>#item.FirstName</td>
<td>#item.LastName</td>
<td>#item.StartDate.ToShortDateString()</td>
<td>
#if (item.EndDate.HasValue)
{
#item.EndDate.Value.ToShortDateString()
}
</td>
<td>
<a class="details_link" data-target-id="#item.Id_Person">Details</a>
</td>
<td>
<div>
<button class="btn btn-primary edit-person" data-id="#item.Id_Person">Edit</button>
</div>
</td>
</tr>
<tr>
<td colspan="6">
<table>
<tr>
<th>National Number</th>
<td>#item.NumNat</td>
</tr>
<tr>
<th>Vehicle Category</th>
<td>#item.ProductPackageCategory.Name</td>
</tr>
<tr>
<th>Upgrade</th><td>#item.Upgrade</td>
</tr>
<tr>
<th>House to work</th>
<td>#item.HouseToWorkKilometers.ToString("G29")</td>
</tr>
</table>
<div id="details_#item.Id_Person"></div>
</td>
</tr>
}
</tbody>
</table>
<div class="modal hide fade in" id="edit-member">
<div id="edit-person-container"></div>
</div>
#section Scripts
{
#Scripts.Render("~/bundles/jqueryui")
#Styles.Render("~/Content/themes/base/css")
<script type="text/javascript" language="javascript">
$(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');
});
});
});
</script>
}
My Partial View :
#model BuSIMaterial.Models.Person
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Edit</h3>
</div>
<div>
#using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "list-of-people"
}))
{
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<div class="modal-body">
<div class="editor-field">
#Html.TextBoxFor(model => model.FirstName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<div class="modal-footer">
<button class="btn btn-inverse" type="submit">Save</button>
</div>
}
You should use partial views. I use the following approach:
Use a view model so you're not passing your domain models to your views:
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; }
}
In your 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");
}
}
Next create a partial view called _EditPersonPartial. This contains the modal header, body and footer. It also contains the Ajax form. It's strongly typed and takes in our view model.
#model Namespace.ViewModels.EditPersonViewModel
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Edit group member</h3>
</div>
<div>
#using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "list-of-people"
}))
{
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<div class="modal-body">
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Name)
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Age)
</div>
<div class="modal-footer">
<button class="btn btn-inverse" type="submit">Save</button>
</div>
}
Now somewhere in your application, say another partial _peoplePartial.cshtml etc:
<div>
#foreach(var person in Model.People)
{
<button class="btn btn-primary edit-person" data-id="#person.PersonId">Edit</button>
}
</div>
// this is the modal definition
<div class="modal hide fade in" id="edit-person">
<div id="edit-person-container"></div>
</div>
<script type="text/javascript">
$(document).ready(function () {
$('.edit-person').click(function () {
var url = "/Person/EditPerson"; // the url to the controller
var id = $(this).attr('data-id'); // the id that's given to each button in the list
$.get(url + '/' + id, function (data) {
$('#edit-person-container').html(data);
$('#edit-person').modal('show');
});
});
});
</script>
I prefer to avoid using Ajax.BeginForm helper and do an Ajax call with JQuery. In my experience it is easier to maintain code written like this. So below are the details:
Models
public class ManagePeopleModel
{
public List<PersonModel> 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
}
Parent View
This view contains the following things:
records of people to iterate through
an empty div that will be populated with a modal when a Person needs to be edited
some JavaScript handling all ajax calls
#model ManagePeopleModel
<h1>Manage People</h1>
#using(var table = Html.Bootstrap().Begin(new Table()))
{
foreach(var person in Model.People)
{
<tr>
<td>#person.Id</td>
<td>#Person.Name</td>
<td>#person.Age</td>
<td>#html.Bootstrap().Button().Text("Edit Person").Data(new { #id = person.Id }).Class("btn-trigger-modal")</td>
</tr>
}
}
#using (var m = Html.Bootstrap().Begin(new Modal().Id("modal-person")))
{
}
#section Scripts
{
<script type="text/javascript">
// Handle "Edit Person" button click.
// This will make an ajax call, get information for person,
// put it all in the modal and display it
$(document).on('click', '.btn-trigger-modal', function(){
var personId = $(this).data('id');
$.ajax({
url: '/[WhateverControllerName]/GetPersonInfo',
type: 'GET',
data: { id: personId },
success: function(data){
var m = $('#modal-person');
m.find('.modal-content').html(data);
m.modal('show');
}
});
});
// Handle submitting of new information for Person.
// This will attempt to save new info
// If save was successful, it will close the Modal and reload page to see updated info
// Otherwise it will only reload contents of the Modal
$(document).on('click', '#btn-person-submit', function() {
var self = $(this);
$.ajax({
url: '/[WhateverControllerName]/UpdatePersonInfo',
type: 'POST',
data: self.closest('form').serialize(),
success: function(data) {
if(data.success == true) {
$('#modal-person').modal('hide');
location.reload(false)
} else {
$('#modal-person').html(data);
}
}
});
});
</script>
}
Partial View
This view contains a modal that will be populated with information about person.
#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" })
}
}
Controller Actions
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);
}
In reply to Dimitrys answer but using Ajax.BeginForm the following works at least with MVC >5 (4 not tested).
write a model as shown in the other answers,
In the "parent view" you will probably use a table to show the data.
Model should be an ienumerable. I assume, the model has an id-property. Howeverm below the template, a placeholder for the modal and corresponding javascript
<table>
#foreach (var item in Model)
{
<tr> <td id="editor-success-#item.Id">
#Html.Partial("dataRowView", item)
</td> </tr>
}
</table>
<div class="modal fade" id="editor-container" tabindex="-1"
role="dialog" aria-labelledby="editor-title">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content" id="editor-content-container"></div>
</div>
</div>
<script type="text/javascript">
$(function () {
$('.editor-container').click(function () {
var url = "/area/controller/MyEditAction";
var id = $(this).attr('data-id');
$.get(url + '/' + id, function (data) {
$('#editor-content-container').html(data);
$('#editor-container').modal('show');
});
});
});
function success(data,status,xhr) {
$('#editor-container').modal('hide');
$('#editor-content-container').html("");
}
function failure(xhr,status,error) {
$('#editor-content-container').html(xhr.responseText);
$('#editor-container').modal('show');
}
</script>
note the "editor-success-id" in data table rows.
The dataRowView is a partial containing the presentation of an model's item.
#model ModelView
#{
var item = Model;
}
<div class="row">
// some data
<button type="button" class="btn btn-danger editor-container" data-id="#item.Id">Edit</button>
</div>
Write the partial view that is called by clicking on row's button (via JS $('.editor-container').click(function () ... ).
#model Model
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="editor-title">Title</h4>
</div>
#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)
<div class="modal-body">
<div class="form-horizontal">
// Models input fields
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
}
This is where magic happens: in AjaxOptions, UpdateTargetId will replace the data row after editing, onfailure and onsuccess will control the modal.
This is, the modal will only close when editing was successful and there have been no errors, otherwise the modal will be displayed after the ajax-posting to display error messages, e.g. the validation summary.
But how to get ajaxform to know if there is an error? This is the controller part, just change response.statuscode as below in step 5:
the corresponding controller action method for the partial edit modal
[HttpGet]
public async Task<ActionResult> 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<ActionResult> 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);
}
This way, if an error occurs while editing Model data in a modal window, the error will be displayed in the modal with validationsummary methods of MVC; but if changes were committed successfully, the modified data table will be displayed and the modal window disappears.
Note: you get ajaxoptions working, you need to tell your bundles configuration to bind jquery.unobtrusive-ajax.js (may be installed by NuGet):
bundles.Add(new ScriptBundle("~/bundles/jqueryajax").Include(
"~/Scripts/jquery.unobtrusive-ajax.js"));
In $('.editor-container').click(function (){}), shouldn't var url = "/area/controller/MyEditAction"; be var url = "/area/controller/EditPartData";?

Categories