Refresh partial view content when submit button clicked - c#

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

Related

I need to run Action Method in Partial View but it redirect me to another action

I need to run Partial View from action method as a child action method but it redirect me to another view.
1- I tried to use Html.Action("myAction","myController") and I use [ChildActionOnly] data annotation but without any benefit
2- I tried to use Html.RenderAction("myAction", "My Controller") and I change the action method as PartialViewResult and return PartialView("View", myData) but without any benefit.
3- I tried to use JQuery AJAX but without any benefit also.
What I get
What I Expect
** This is the controller
[Authorize(Roles = "Admin")]
[HttpPost]
[ChildActionOnly]
public ActionResult _GetUserRoles(string UserName)
{
SqlParameter param1 = new SqlParameter("#UserName", UserName);
try
{
IList<GetUserRolesViewModel> roles = Identitydb.Database.SqlQuery<GetUserRolesViewModel>("admin.sp_GetUserRoles #UserName",
((ICloneable)param1).Clone()).ToArray().ToList();
return View(roles);
}
catch (Exception ex)
{
ViewBag.Error = ex.ToString();
return RedirectToAction("ErrorSaveData");
}
}
** This is PartialView Code
#model IEnumerable<AMSIdentity.Controllers.GetUserRolesViewModel>
#if (Model == null)
{
<table></table>
}
else
{
<table class="table table-responsive table-striped table-hover table-bordered table-condensed container" style="margin-top: 5%;">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Id)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
</tr>
}
</tbody>
</table>
<br />
#Html.ActionLink("Return Back", "RemoveRoleFromUser", "Manage")
}
** This is Parent Page Code
ViewBag.Title = "RemoveRoleFromUser";
var error = ViewBag.Error as IEnumerable<String>;
}
<h2> Remove role from user </h2>
<ul></ul>
<div class="row container">
<div class="col-md-6">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Username", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("UserName", new { htmlAttributes = new { #class = "form-control" } })
</div>
<hr />
#Html.Label("Role Id", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("RoleId", new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Remove" class="btn btn-default btn-danger" />
</div>
</div>
</div>
}
</div>
<div class="col-md-6">
<div class="row container">
<div class="col-md-12">
<h3> Get user roles </h3>
#using (Html.BeginForm("_GetUserRoles", "Manage", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Username", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("UserName", new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Get rules" class="btn btn-default btn-success" id="btnRules"/>
</div>
</div>
</div>
}
</div>
</div>
<div class="row container">
<div class="col-md-12">
#Html.Partial("_GetUserRoles")
</div>
</div>
</div><!--Second Column-->
</div> <!--End of row-->
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
** This is the view Model
public class GetUserRolesViewModel
{
[DisplayName("Username")]
public string Name { get; set; }
[Key]
[DisplayName("Role Id")]
public string Id { get; set; }
}
** This is AJAX Code that i used to run partial view
<script type="text/javascript">
//$(document).ready(function ()
//{
$("#btnRules").click(function (e)
{
var UserName = $("#UserName").val();
$.ajax({
url: '/Manage/_GetUserRoles',
dataType: 'html',
data:{"UserName": UserName},
success: function (data)
{
$('#listRules').html(data);
},
error: function (xhr, ajaxOptions, thrownError)
{
alert('Failed to retrieve rules.');
}
});
});
//});
</script>
** Route Picture
Route Config Picture
I expect to run the Action Method (_GetUserRoles) Partially in the same view of RemoveRoleFromUser.
your return must be a PartialView() and not a view. view() will return a complete view where as PartialView() will return a partial view. So yo need to change your
return View();
to
return PartialView("YourPartialViewName");
and if you need to pass a parameter to your partial view
return PartialView("YourPartialViewName",roles);
if you are rendering a partial view from your View them you can use Html hepler
return PartialView("~/views/ABC/PartialViewName.cshtml", Yourmodel);
I am assuming that your ajax call hits the controller action. And the partial view needs to be placed in the parent view code at-
<div class="row container">
<div class="col-md-12">
#Html.Partial("_GetUserRoles")
</div>
</div>
Based on these assumptions, you need to change few things in your code:
return PartialView instead of View from the action. Something like this:
return PartialView("_GetUserRoles", roles);
// the partial view name mentioned here is just a sample based on the context.
//You need to give your partial view's name here.
add an id attribute to the div inside which you have written #Html.Partial("_GetUserRoles"). Something like this:
<div class="row container">
<div class="col-md-12" id="userRoles">
</div>
</div>
within the success function in the ajax method, you need to use the id attribute set in above step and populate the data inside it. Something like this:
success: function (data)
{
$('#userRoles').html(data);
}
Install the Microsoft.Jquery.Unobtrusive.Ajax and Microsoft.Jquery.Unobtrusive.Validation in your project. Then add the reference of these two within your view file. Something like this:
#section scripts{
#Scripts.Render("~/Scripts/jquery{version}.js")
#Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
}
This way you should be able to see the partial view within your existing view itself.
Here is the answer
I use ViewData[""] rather than PartialView and i put one parameter in the same action result to check which view i will use.
** Action Method RemoveRoleFromUserConfirmed
public ActionResult RemoveRoleFromUserConfirmed(string UserName, string RoleId, string btnval)
{
if (btnval == "Remove")
{
if (UserName == null && RoleId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
SqlParameter param1 = new SqlParameter("#RoleId", RoleId);
SqlParameter param2 = new SqlParameter("#UserName", UserName);
try
{
Identitydb.Database.ExecuteSqlCommand("admin.sp_RemoveUserFromRole #RoleId, #UserName", param1, param2);
}
catch (Exception ex)
{
ViewBag.Error = ex.ToString();
return RedirectToAction("ErrorSaveData");
}
return RedirectToAction("Roles");
}
else if (btnval == "Get Rules")
{
SqlParameter param1 = new SqlParameter("#UserName", UserName);
try
{
IList<GetUserRolesViewModel> roles = Identitydb.Database.SqlQuery<GetUserRolesViewModel>("admin.sp_GetUserRoles #UserName",
((ICloneable)param1).Clone()).ToArray().ToList();
string result = "";
foreach (var item in roles)
{
result += "<tr>"
+ "<td>" + item.Name + "</td>"
+ "<td>" + item.Id + "</td>"
+ "</tr>";
}
ViewData["getrules"] = result;
return View();
}
catch (Exception ex)
{
ViewBag.Error = ex.ToString();
return RedirectToAction("ErrorSaveData");
}
}
return View();
}
** This is the view
#{
ViewBag.Title = "RemoveRoleFromUser";
var error = ViewBag.Error as IEnumerable<String>;
}
<h2> Remove role from user </h2>
<ul></ul>
<div class="row container">
<div class="col-md-6">
#using (Html.BeginForm("RemoveRoleFromUser", "Manage", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Username", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("UserName", new { htmlAttributes = new { #class = "form-control" } })
</div>
<hr />
#Html.Label("Role Id", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("RoleId", new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Remove" name="btnval" class="btn btn-default btn-danger" />
</div>
</div>
</div>
}
</div>
<div class="col-md-6">
<div class="row container">
<div class="col-md-12">
<h3> Get user roles </h3>
#using (Html.BeginForm("RemoveRoleFromUser", "Manage", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Username", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Editor("UserName", new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Get Rules" name="btnval" class="btn btn-default btn-success" id="btnRules" />
</div>
</div>
</div>
}
</div>
</div>
<div class="row container">
<div class="col-md-12" id="listRules">
<!--Data Come from ViewData-->
<table class="table table-responsive table-striped table-hover table-bordered table-condensed container" style="margin-top: 5%;">
<thead>
<tr>
<th>
Role Name
</th>
<th>
Role Id
</th>
</tr>
</thead>
<tbody>
#Html.Raw(ViewData["getrules"])
</tbody>
</table>
</div>
</div>
</div><!--Second Column-->
</div> <!--End of row-->
<div>
#Html.ActionLink("Back to List", "Roles")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
I put the name of button same in two buttons (remove and get rules)
after that i checked the value of the button in ActionMethod and if it
is remove it will do some function and if it is get rules i will use
ViewData[""] to put the view.
Regards,
Ali Mosaad
Software Developer
I would suggest changing
return View(roles);
to
return PartialView(roles);
or
return PartialView("_GetUserRoles", roles);
Using ajax
$.ajax({
url: '/Manage/_GetUserRoles',
dataType: 'html',
data:{"UserName": UserName},
success: function (data)
{
$('#listRules').html(data);
},
error: function (xhr, ajaxOptions, thrownError)
{
alert('Failed to retrieve rules.');
}
});
...
<div class="row container">
<div id="listRules" class="col-md-12">
#Html.Partial("_GetUserRoles", Model.Roles)
</div>
</div>

Save the listbox selected items id into database

I want to pass list of ids into sql database in ASP MVC. In here there are two listbox where from one to other listbox able pass selected course names and then need to save those course ids with including the student id. How is this need to do using the jQuery? please help me on this. Here is my code upto now..
Create View
Create
.....
using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>StudentCourseVM</legend>
<div class="editor-label">
#Html.LabelFor(model => model.StudentId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StudentId)
#Html.ValidationMessageFor(model => model.StudentId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.StudentName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StudentName)
#Html.ValidationMessageFor(model => model.StudentName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.StudentAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StudentAddress)
#Html.ValidationMessageFor(model => model.StudentAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CourseName)
</div>
<div class="editor-field">
<table>
<tr>
<td> Available Courses <br />
<select multiple="multiple" id="listboxCourse">
#foreach (var item in Model.courseList)
{
<option title="#item.CourseName" id="#item.CourseID">
#item.CourseName
</option>
}
</select>
</td>
<td>
<button type="button" id="btnAdd">Move Right>></button>
<br/>
<button type="button" id ="btnRemove">Move Left<<</button>
</td>
<td> Selected Courses<br />
<select multiple="multiple" id="addcourselist" >
</select>
</td>
</tr>
</table>
</div>
<p>
<input type="submit" value="Create" id="Create" />
</p>
</fieldset>
}
<script>
$(document).ready(function() {
$("#btnAdd").click(function() {
$("#listboxCourse > option:selected").appendTo("#addcourselist");
});
$("#btnRemove").click(function() {
$("#addcourselist > option:selected").appendTo("#listboxCourse");
});
})
$(function () {
$("#Create").click(function () {
var listCourse = new Array();
for (var i = 0; i < addcourselist.length ; i++) {
listCourse.push(Option[i].value);
}
});
$.ajax({
type: "POST",
data: listCourse,
dataType : "json",
url: "Student/Create",
success: function (data) {
alert("Code is running...............");
}
});
})
Controller
[HttpGet]
[ActionName("Create")]
public ActionResult Create()
{
StudentCourseVM studentcourse = new StudentCourseVM();
StudentBusinessLayer studentBusinessLayer = new StudentBusinessLayer();
List<Course> coursestudentList = studentBusinessLayer.CourseList.ToList();
studentcourse.courseList = coursestudentList;
return View(studentcourse);
}
[HttpPost]
[ActionName("Create")]
public ActionResult Create(StudentCourseVM studentcourse)
{
if (ModelState.IsValid)
{
StudentBusinessLayer studentBusinessLayer = new StudentBusinessLayer();
List<Course> coursestudentList = studentBusinessLayer.CourseList.ToList();
studentcourse.courseList = coursestudentList;
studentBusinessLayer.AddStudent(studentcourse);
return RedirectToAction("Index");
}
return View();
}
How am i supposed to do this. Two listbox are like this. The items in second listbox need to be saved in database with the relate studentid.
You can save it as JSON (objets) strings
Check out How To Populate Html.Listbox with Ajax result using mvc 4

Not able to render partial view as a part of view

There are a couple of questions I referred on rendering partial views in mvc4. But my problem is that I have a division(DisplayRecords) in my view and want the partial view to open in that division instead of a different view on click of 'view' button.
But that is not happening because in the controller I am returning the partial view because if I return the view, I cannot pass the list containing data.
On returning view, it throws an error that the view cannot accept that type of parameter and the partial view will not work without the list being passed.
I tried assigning the list to a model property and pass that property value to partial view from client side but that throws a null reference exception.
Can someone suggest how I can fix this.
Here is what I have written:
My view:
#model mvcEmail.Models.EmailModel
#*<script type="text/javascript">
var command = document.getElementById("FirstName");
alert(command);
if (command == "Value")
$('#DisplayRecords').load('/Email/Index2');
</script>*#
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (#Html.BeginForm())
{
<div>
#* #Html.Label(Model.Acknowledge, new { id = "Acknowledge" })
<br />*#
#Html.TextBoxFor(model => model.FirstName, new { id = "FirstName", #class = "ClassName" })
<br />
#Html.TextBoxFor(model => model.PhoneNumber, new { id = "PhoneNumber", #class = "ClassPhoneNumber" })
<br />
<input type="submit" name="Command" value="Submit" />
<input type="submit" name="Command" value="View" />
</div>
<div id="DisplayRecords">
#if (ViewBag.query == "View")
{
#Html.Partial("Index2")
}
</div>
}
My partial view:
#model IEnumerable<mvcEmail.Models.EmailModel>
<p>
#Html.ActionLink("Create New", "Index")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.PhoneNumber)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PhoneNumber)
</td>
#* <td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>*#
</tr>
}
</table>
My controller where I am passing list to view:
if (Command == "View")
{
ViewBag.query = "View";
//var records = new DisplayRecords();
List<EmailModel> recs = new List<EmailModel>();
cmd.CommandText = "DisplayRecords";
cmd.Connection = con;
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
model = new EmailModel();
model.PhoneNumber = reader["phone"].ToString();
model.FirstName = reader["Name"].ToString();
recs.Add(model);
}
con.Close();
con.Dispose();
return PartialView("Index2", recs);
//return View(recs);
}
Your Partial view is not getting its instance of model.You need to provide your model during calling partial view in view as -
#Html.Partial("Index2", new List<mvcEmail.Models.EmailModel>(){this.Model})
or I will recommend to write another controller action which will return your partial view with model as -
public ActionResult PartialView()
{
List<mvcEmail.Models.EmailModel> emailListModel = new List<mvcEmail.Models.EmailModel>();
return PartialView(emailListModel);
}
and then call it using -
#{Html.RenderAction("PartialView", "Controller_name",null);}
This will make your code better as with single responsibility principle.
I hope this will help you.

Error render PartialView "The model item passed into the dictionary is of type"

I want to render login page if user session is over, this is my code :
#model IEnumerable<LPDPWebApp.User>
#{
Layout = "~/Views/Shared/_LayoutDashboard.cshtml";
String datenow = DateTime.Now.ToShortDateString();
Boolean IsAuthorized = false;
if (Session["username"] != null)
{
IsAuthorized = true;
Layout = "~/Views/Shared/_LayoutDashboard.cshtml";
}
else
{
Layout = "~/Views/Shared/_LayoutLogin.cshtml";
}
}
#if (IsAuthorized)
{
<div class="side-b">
<div class="container-fluid">
<div class="row page-title">
<div class="col-md-12">
<button type="button" class="pull-right btn-layout-modal" id="modal-open" data-toggle="modal" data-target="#layout-modal">
<i class="fa fa-th fa-fw"></i>
</button>
<h4>Dana Kegiatan Pendidikan</h4>
<p>#datenow</p>
</div>
</div>
<div class="row grid-sortable">
Create
<table class="table">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
#Html.ActionLink("Details", "Details", new { id = item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
</div>
</div>
</div>
}
else
{
#Html.Partial("_LoginPartial")
}
_LoginPartial.cshtml
#model LPDPWebApp.Models.Access.LoginModel
#using (Html.BeginForm("Login", "Access", FormMethod.Post, new { id = "formLogin", #class = "form-vertical" }))
{
#Html.AntiForgeryToken()
<div class="col-md-4">
#Html.TextBoxFor(m => m.Username, new { placeholder = "username", #class = "form-control", id = "tbxUsername" })
</div>
<div class="col-md-4">
#Html.PasswordFor(m => m.Password, new { placeholder = "password", #class = "form-control", id = "tbxPwd" })
</div>
<div class="col-md-8">
<button type="submit" class="btn btn-primary pull-right btn-submit">submit</button>
</div>
}
but i get error on #Html.Partial("_LoginPartial") :
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[LPDPWebApp.User]', but this
dictionary requires a model item of type
'LPDPWebApp.Models.Access.LoginModel'.
I just want to render _LoginPartial if Session is over, how to solve this problem?
Your _LoginPartial is expecting a model of type LoginModel:
#model LPDPWebApp.Models.Access.LoginModel
But when you're calling the #Html.Partial method:
#Html.Partial("_LoginPartial")
You don't specify any model hence the parent model (IEnumerable<LPDPWebApp.User>) is used and the exception is thrown because they don't match.
Try this:
#Html.Partial("_LoginPartial", new LoginModel());
It's also seems like you don't really use the model in your _LoginPartial, then you can even pass a null model to it:
#Html.Partial("_LoginPartial", null);

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