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>
Related
I've got a list of items on a page, capable of being filtered by a textbox (similar to the MVC tutorial). I have a button which I would like to clear the text in the TextBox and call the controller function which will then bring the list of items back to its initial, unifiltered state.
The problem I am having is twofold: my jQuery code which clears the text of the TextBox does not clear the TextBox of the search term, and my jQuery code which explicitly passes in the empty string as a parameter to my controller does not pass in that parameter.
For example:
Page has a list of stuff.
I type "fart" in the filter box, and hit enter.
The page reloads, and the list of stuff is now filtered by "fart".
I press the Clear button.
The page reloads, but the list of stuff is still filtered by "fart", and the TextBox is not cleared.
I suspect this is all due to the TextBox not being cleared, but I honestly have no idea.
Index.cshtml
#{
ViewBag.Title = "Index";
var modelitem = new MyThing();
}
#model IEnumerable<MyModel>
<script type="text/javascript">
function fart() {
$('#reset').click(function() {
$('#filterTerm').val('');
$.get("SelectionSummary/Index", { filterTerm: '' })
});
}
</script>
#using (Html.BeginForm())
{
<div class="input-group center-block">
<h3 class="text-center">Selections</h3>
#Html.AntiForgeryToken()
#Html.TextBox("filterTerm", null, new
{
#id = "filterTerm",
#class = "form-control col-md-offset-2",
#placeholder = "Filter"
})
<input id="reset" class="btn btn-info" type="submit" value="reset" onclick="fart"/>
</div>
}
<br />
#if (Model != null && Model.Any())
{
<div class="panel panel-default col-md-offset-2" style="width:62%">
<div class="panel-body">
<br />
<table class="table table-striped text-center">
<tr>
<th>
#Html.DisplayFor(m => modelitem.NameLabel)
</th>
<th>
#Html.DisplayFor(m => modelitem.Thing)
</th>
</tr>
#foreach (var thing in Model.OrderBy(g => g.Name))
{
<tr>
<th>
#Html.ActionLink(thing.Name, "Detail", new { selectionSearchTerm = thing.Name })
</th>
<th>
#Html.DisplayFor(m => thing.Other.Name)
</th>
</tr>
}
</table>
</div>
</div>
}
else
{
<h4 class="text-center">No results</h4>
}
SelectionSummaryController.cs
public ActionResult Index(string filterTerm = "")
{
var stuff= m_repo.GetAllStuff();
if (stuff.IsNullOrEmpty()) // extension method
{
return View();
}
if (filterTerm.HasValue()) // extension method
{
stuff= stuff.Where(t => t.Name.Contains(filterTerm));
}
return View(stuff.ToList());
}
I believe your problem is in using HasValue. I would change to:
public ActionResult Index(string filterTerm = "")
{
var stuff= m_repo.GetAllStuff();
if (stuff.IsNullOrEmpty())
{
return View();
}
if (!string.IsNullOrEmpty(filterTerm))
{
stuff= stuff.Where(t => t.Name.Contains(filterTerm));
}
return View(stuff.ToList());
}
Furthermore, your javascript doesn't make sense. Why are you putting a click event handler on a button when you click the button? Try this.
<script type="text/javascript">
function fart() {
$('#filterTerm').val('');
$.get("SelectionSummary/Index", { filterTerm: '' })
}
</script>
That being said, I am not sure why you are even using that ajax get. You aren't doing anything with the data.
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>
I've got these two cascading dropdown lists, they populate just fine. But when I hit submit I always gett NULL as the value of the second. My guess is that I have to do something with the javascript, but my skills in that department are seriously lacking. Please help!
Code removed, whole view added instead
I dont even have an HtmlHelper for the second dropdown, it just looks like this in the view:
Code removed, whole view added instead
Works just fine, I mean, it populates fine depending on what's chosen in the first dropdown. Maybe this is the part that needs alteration? The thing is I'm clueless.
EDIT:
This is the code that reads the information from the form and submits it to the database.
[HttpPost]
public ActionResult Returer(ReturerDB retur)
{
if (ModelState.IsValid)
{
db2.ReturerDB.AddObject(retur);
db2.SaveChanges();
return RedirectToAction("Returer");
}
return View("Returer");
}
EDIT2
The whole view code:
#model Fakturabolag.Models.ReturerDB
#{
ViewBag.Title = "Returer";
}
<script type="text/javascript" src="../../Scripts/jquery-1.7.1.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#bolag").prop("disabled", true);
$("#Kund").change(function () {
if ($("#Kund").val() != "- Välj bolag -") {
var options = {};
options.url = "/companies/getbolag";
options.type = "POST";
options.data = JSON.stringify({ country: $("#Kund").val() });
options.dataType = "json";
options.contentType = "application/json";
options.success = function (bolag) {
$("#bolag").empty();
for (var i = 0; i < bolag.length; i++) {
$("#bolag").append("<option>" + bolag[i] + "</option>");
}
$("#bolag").prop("disabled", false);
};
options.error = function () { alert("Fel vid bolagshämtning!"); };
$.ajax(options);
}
else {
$("#bolag").empty();
$("#bolag").prop("disabled", true);
}
});
});
</script>
<h2>Returer</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Returer</legend>
<br /><br />
<div class="editor-label">
<b>Kund</b>
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Kund, ViewData["kundLista"] as SelectList)
#Html.ValidationMessageFor(model => model.Kund)
</div>
<div class="editor-label">
<b>Bolag</b>
</div>
<select id="bolag"></select>
<div class="editor-label">
<b>Pris</b>
</div>
<div class="editor-field">
#Html.TextBox("pris", null, new { style = "width: 150px" })
#Html.ValidationMessageFor(model => model.Pris)
</div>
<div class="editor-label">
<b>Datum</b>
</div>
<div class="editor-field">
#Html.TextBox("datum", ViewData["datum"] as String, new { style = "width: 150px" })
#Html.ValidationMessageFor(model => model.Datum)
</div>
<p>
<input type="submit" value="Lägg till" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Currently you are not retrieving the additional value "bolag" in your action. I guess your model does not have property named "bolag". You can either add that or you can add additional parameter to you action like so:
[HttpPost]
public ActionResult Returer(ReturerDB retur, string bolag)
{
if (ModelState.IsValid)
{
db2.ReturerDB.AddObject(retur);
db2.SaveChanges();
return RedirectToAction("Returer");
}
return View("Returer");
}
After that, the selected value from the dropdown should automatically be in the bolag-parameter.
Edit.
You also need to add name-attribute to your select:
<select id="bolag" name="bolag"/>
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";?
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);
}