MVC Paging not working in AJAX Form - c#

I am trying to implement ASP.NET MVC Paging using MVC4.Paging nuget package.
Problem:
It is working in online demo and also in the download source code. However I am not able to find why it is not working in my particular project by AJAX. In my project it is working by full post back method but not Ajax.
I have even tried to copy over the Index action method and Index and _AjaxEmployeeList Views (.cshtml) files (except .js).
Note: In my solution its not bootstrap as shown in samples. Also my controller name is AdminController whereas in Samples its HomeController
In my solution I need it to work in AJAX method as in samples.
Kindly help regarding:
1. How to find the root cause for why it is not working?
2. How to make this working?
.
My Solution Code (which I tried to reproduce in my solution from the sample):
Index.cshtml
#using MvcPaging
#model IPagedList<MvcPagingDemo.Models.Employee>
#{
ViewBag.Title = "MVC 4 Paging With Ajax Bootstrap";
}
<div class="row-fluid">
<div class="span6">
<h2>
Ajax Paging With Bootstrap In MVC 4
</h2>
</div>
<div class="span6">
<div class="alert alert-info">
The pager also supports area's. #Html.ActionLink("Ajax paging in an area", "Index", "Admin", new { Area = "AreaOne" }, new { #class = "btn btn-success" })</div>
</div>
</div>
<div class="clearfix">
</div>
#using (Ajax.BeginForm("Index", "Admin",
new AjaxOptions { UpdateTargetId = "grid-list", HttpMethod = "get", LoadingElementId = "loading", OnBegin = "beginPaging", OnSuccess = "successPaging", OnFailure = "failurePaging" },
new { id = "frm-search" }))
{
<div class="input-append">
<input class="span2" id="appendedInputButton" type="text" name="employee_name" placeholder="Name" />
<button class="btn" type="submit">
<i class="icon-search"></i> Search</button>
</div>
<div id="grid-list">
#{ Html.RenderPartial("_AjaxEmployeeList", Model); }
</div>
}
<script type="text/javascript">
function beginPaging(args) {
// Animate
$('#grid-list').fadeOut('normal');
}
function successPaging() {
// Animate
$('#grid-list').fadeIn('normal');
$('a').tooltip();
}
function failurePaging() {
alert("Could not retrieve list.");
}
</script>
_AjaxEmployeeList.cshtml
#using MvcPaging
#model IPagedList<MvcPagingDemo.Models.Employee>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Email
</th>
<th>
Phone
</th>
<th>
City
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#item.ID
</td>
<td>
#item.Name
</td>
<td>
#item.Email
</td>
<td>
#item.Phone
</td>
<td>
#item.City
</td>
</tr>
}
</tbody>
</table>
<div class="pager1">
#Html.Raw(Ajax.Pager(
new Options
{
PageSize = Model.PageSize,
TotalItemCount = Model.TotalItemCount,
CurrentPage = Model.PageNumber,
ItemTexts = new ItemTexts() { Next = "Next", Previous = "Previous", Page = "P" },
ItemIcon = new ItemIcon() { First = "icon-backward", Previous = "icon-chevron-left", Next = "icon-chevron-right", Last = "icon-forward" },
TooltipTitles = new TooltipTitles() { Next = "Next page", Previous = "Previous page", Page = "Page {0}." },
Size = Size.normal,
Alignment = Alignment.centered,
IsShowControls = true,
IsShowFirstLast = true,
CssClass = ""
},
new AjaxOptions
{
UpdateTargetId = "grid-list",
OnBegin = "beginPaging",
OnSuccess = "successPaging",
OnFailure = "failurePaging"
}, new { controller = "Admin", action = "Index", employee_name = ViewData["employee_name"] }))
<div class="well">
Showing <span class="badge badge-success">#Model.ItemStart</span> to <span class="badge badge-success">#Model.ItemEnd</span>
of <span class="badge badge-info">#Model.TotalItemCount</span> entries</div>
</div>
AdminController.cs
public class AdminController : Controller
{
private const int defaultPageSize = 3;
private IList<Employee> allEmployee = new List<Employee>();
private string[] name = new string[4] { "Will", "Johnny", "Zia", "Bhaumik" };
private string[] phone = new string[4] { "1-274-748-2630", "1-762-805-1019", "1-920-437-3485", "1-562-653-8258" };
private string[] email = new string[4] { "donec#congueelitsed.org", "neque.non#Praesent.co.uk", "et.magna#Pellentesque.ca", "enim.commodo#orci.net" };
private string[] city = new string[4] { "Wigtown", "Malderen", "Las Vegas", "Talence" };
public AdminController()
{
InitializeEmployee();
}
private void InitializeEmployee()
{
// Create a list of 200 employee.
int index = 0;
for (int i = 0; i < 200; i++)
{
var employee = new Employee();
//int categoryIndex = i % new Random().Next(1, 5);
//if (categoryIndex > 3)
// categoryIndex = 3;
index = index > 3 ? 0 : index;
employee.ID = i + 1;
employee.Name = name[index];
employee.Phone = phone[index];
employee.Email = email[index];
employee.City = city[index];
allEmployee.Add(employee);
index++;
}
}
public ActionResult Index(string employee_name, int? page)
{
ViewData["employee_name"] = employee_name;
int currentPageIndex = page.HasValue ? page.Value : 1;
IList<Employee> employees = this.allEmployee;
if (string.IsNullOrWhiteSpace(employee_name))
{
employees = employees.ToPagedList(currentPageIndex, defaultPageSize);
}
else
{
employees = employees.Where(p => p.Name.ToLower() == employee_name.ToLower()).ToPagedList(currentPageIndex, defaultPageSize);
}
//var list =
if (Request.IsAjaxRequest())
return PartialView("_AjaxEmployeeList", employees);
else
return View(employees);
}
public ActionResult Paging(string employee_name, int? page)
{
ViewData["employee_name"] = employee_name;
int currentPageIndex = page.HasValue ? page.Value : 1;
IList<Employee> employees = this.allEmployee;
if (string.IsNullOrWhiteSpace(employee_name))
{
employees = employees.ToPagedList(currentPageIndex, defaultPageSize);
}
else
{
employees = employees.Where(p => p.Name.ToLower() == employee_name.ToLower()).ToPagedList(currentPageIndex, defaultPageSize);
}
return View(employees);
}
}
JS references in the _Layout.cshtml

You have not described how exactly it is not working. but i will guess the most common issues which might be causing your issue
make sure you actually have a reference to the correct java script files. its not enough to have them in a folder, your page must link to them.
see the following link to see how you reference scripts on you page. http://www.w3schools.com/tags/att_script_src.asp
make sure you put in the correct path.
make sure you reference the *ajax.js files for ajax to work along with any other required files.

Finally I found the solution.
Since I was using jquery-1.11.1.js, in script file jquery.unobtrusive-ajax.js I had to replace calls to .live() with .on().
But simple find and replace was not right way which I found later. I found from other sources that I have to change completely those lines of code as the .on() works.
I replaced the code as below:
Non-working code with .live() function:
$("a[data-ajax=true]").live("click", function (evt) {
debugger;
evt.preventDefault();
asyncRequest(this, {
url: this.href,
type: "GET",
data: []
});
});
$("form[data-ajax=true] input[type=image]").live("click", function (evt) {
debugger;
var name = evt.target.name,
$target = $(evt.target),
form = $target.parents("form")[0],
offset = $target.offset();
$(form).data(data_click, [
{ name: name + ".x", value: Math.round(evt.pageX - offset.left) },
{ name: name + ".y", value: Math.round(evt.pageY - offset.top) }
]);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$("form[data-ajax=true] :submit").live("click", function (evt) {
debugger;
var name = evt.target.name,
form = $(evt.target).parents("form")[0];
$(form).data(data_click, name ? [{ name: name, value: evt.target.value }] : []);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$("form[data-ajax=true]").live("submit", function (evt) {
debugger;
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault();
if (!validate(this)) {
return;
}
asyncRequest(this, {
url: this.action,
type: this.method || "GET",
data: clickInfo.concat($(this).serializeArray())
});
});
Working code with .on() function:
$(document).on("click", "a[data-ajax=true]", function (evt) {
evt.preventDefault();
asyncRequest(this, {
url: this.href,
type: "GET",
data: []
});
});
$(document).on("click", "form[data-ajax=true] input[type=image]", function (evt) {
var name = evt.target.name,
$target = $(evt.target),
form = $target.parents("form")[0],
offset = $target.offset();
$(form).data(data_click, [
{ name: name + ".x", value: Math.round(evt.pageX - offset.left) },
{ name: name + ".y", value: Math.round(evt.pageY - offset.top) }
]);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$(document).on("click", "form[data-ajax=true] :submit", function (evt) {
var name = evt.target.name,
form = $(evt.target).parents("form")[0];
$(form).data(data_click, name ? [{ name: name, value: evt.target.value }] : []);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$(document).on("submit", "form[data-ajax=true]", function (evt) {
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault();
if (!validate(this)) {
return;
}
asyncRequest(this, {
url: this.action,
type: this.method || "GET",
data: clickInfo.concat($(this).serializeArray())
});
});
Thanks.

Related

How to load partial view in <DIV> tag?

I am still learning MVC. I am having problem with Partial View. I have a page with dropdown value. And whenever user choose the value, a partial page with datatable will appear.
Currently, i am thinking of using JQuery load function to load the page into the div tag. But the datatable is not showing. Is there something wrong with my code or is it any better way of doing this? Please help. Thank you.
My View:
#model MVCWeb.Models.DP_Table
#{
ViewBag.Title = "Data Patching";
}
<br />
<h2>#ViewBag.Title</h2>
<br />
<table class="table-striped table-responsive">
<tbody>
<tr>
<td width="40%">
<label class="control-label">Select Table</label>
</td>
<td width="10%">:</td>
<td width="*%">
#Html.DropDownListFor(Model => Model.DPTableID, new SelectList(Model.TableCollection,
"DPTableId", "TableName"), "Please Select", new { #id = "ddTableName", #class = "form-control" })
</td>
</tr>
<tr>
<td><br /></td>
</tr>
<tr>
<td>
<label class="control-label">Select Action</label>
</td>
<td>:</td>
<td>
#Html.DropDownList("Actions", #ViewBag.PatchingActions as List<SelectListItem>,
"Please Select", new { #id = "ddPatchingAction", #class = "form-control", #disabled = "disabled" })
</td>
</tr>
</tbody>
</table>
<br />
<div id="divPatching"></div>
#section scripts{
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script>
var ddTableValue, ddPatchingActionValue;
$('#ddTableName').change(function () {
ddTableValue = $('#ddTableName option:selected').val();
if (ddTableValue) {
$("#ddPatchingAction").prop("disabled", false);
} else {
$("#ddPatchingAction").prop("disabled", true);
}
});
$('#ddPatchingAction').change(function () {
ddPatchingActionValue = $('#ddPatchingAction option:selected').val();
if (ddPatchingActionValue) {
$("#divPatching").load('#Url.Action("GetPartialView", "DataPatching")');
}
});
</script>
}
My Controller:
public PartialViewResult GetPartialView()
{
return PartialView("~/Views/PatchingBatch/Index.cshtml");
}
My Partial View:
<a class="btn btn-success" style="margin-bottom:10px" onclick="AddUserForm('#Url.Action("AddUser", "Account")')"><i class="fa fa-plus"></i> Add New User</a>
<table id="batchTable" class="table-striped table-responsive">
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Email Address</th>
<th>Is Admin</th>
<th></th>
</tr>
</thead>
</table>
<link href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet" />
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
#section scripts{
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script>
var popup, dataTable;
$(document).ready(function () {
dataTable = $("#batchTable").DataTable({
"ajax": {
"url": "/Account/GetDPUserList",
"type": "POST",
"datatype": "json"
},
"columns": [
{ "data": "Username", "name":"Username" },
{ "data": "Name", "name": "Name" },
{ "data": "Email", "name": "Email" },
{ "data": "IsAdmin", "name": "IsAdmin" },
{
"data": "DPUserID", "render": function (data) {
return "<a class='btn btn-primary btn-sm' onclick=EditUserForm('#Url.Action("UpdateUser", "Account")/" + data +"')><i class='fa fa-pencil'></i> Edit</a><a class='btn btn-danger btn-sm' style='margin-left: 5px' onclick=Delete(" + data +")><i class='fa fa-trash'></i> Delete</a>";
},
"orderable": false,
"searchable": false,
"width": "150px"
},
],
"processing": "true",
"serverSide": "true",
"order": [0, "asc"]
});
});
function AddUserForm(url) {
var formDiv = $('<div/>');
$.get(url)
.done(function (response) {
formDiv.html(response);
popup = formDiv.dialog({
autoOpen: true,
resizable: false,
title: "Add User Account",
height: 250,
width: 300,
close: function () {
popup.dialog('destroy').remove();
}
});
});
}
function EditUserForm(url) {
var formDiv = $('<div/>');
$.get(url)
.done(function (response) {
formDiv.html(response);
popup = formDiv.dialog({
autoOpen: true,
resizable: false,
title: "Update User Account",
height: 410,
width: 300,
close: function () {
popup.dialog('destroy').remove();
}
});
});
}
function SubmitForm(form) {
$.validator.unobtrusive.parse(form);
if ($(form).valid()) {
$.ajax({
type: "POST",
url: form.action,
data: $(form).serialize(),
success: function (data) {
if (data.success) {
popup.dialog('close');
dataTable.ajax.reload();
$.notify(data.message, {
globalPosition: "top center",
className: "success"
})
}
}
});
}
return false;
}
function Delete(id) {
if (confirm("Are you sure you want to delete this data?")) {
$.ajax({
type: "POST",
url: '#Url.Action("DeleteUser", "Account")/' + id,
success: function (data) {
if (data.success) {
dataTable.ajax.reload();
$.notify(data.message, {
globalPosition: "top center",
className: "success"
})
}
}
}
)
}
}
</script>
}
My Partial Controller:
public ActionResult Index()
{
return PartialView();
}
[HttpPost]
public ActionResult GetDPUserList()
{
//server side parameter
int start = Convert.ToInt32(Request["start"]);
int length = Convert.ToInt32(Request["length"]);
string searchValue = Request["search[value]"];
string sortColumnName = Request["columns[" + Request["order[0][column]"] + "][name]"];
string sortDirection = Request["order[0][dir]"];
List<DP_User> userList = new List<DP_User>();
using (DBModel db = new DBModel())
{
userList = db.DP_User.ToList<DP_User>();
userList = userList.ToList<DP_User>();
int totalrows = userList.Count();
//search
if (!string.IsNullOrEmpty(searchValue))
{
userList = userList.Where(x => (x.Username != null && x.Username.ToString().ToLower().Contains(searchValue)) ||
(x.Name != null && x.Name.ToString().ToLower().Contains(searchValue)) ||
(x.Email != null && x.Email.ToString().ToLower().Contains(searchValue))).ToList<DP_User>();
}
int totalrowsafterfilter = userList.Count();
//sorting
if (!string.IsNullOrEmpty(sortColumnName) && !string.IsNullOrEmpty(sortDirection))
{
userList = userList.OrderBy(sortColumnName + " " + sortDirection).ToList<DP_User>();
}
//paging
userList = userList.Skip(start).Take(length).ToList<DP_User>();
return Json(new { data = userList, draw = Request["draw"], recordsTotal = totalrows, recordsFiltered = totalrowsafterfilter },
JsonRequestBehavior.AllowGet);
}
}
1-Create One Partial View To Name Of Page
2-Create One Action
public ActionResult YourPage()
{
return PartialView("~/Views/Page.cshtml");
}
3-To Your View Create One div
<div id="MyDiv"></div>
4-Write This Code To script
$(document).ready(function () {
$.get("/Home/YourPage", function (data) {
$('#MyDiv').html(data);
});
});
If you are in ASP.NET core you can use this command
<partial name="_yourPartial" />
it will load the view for you from a method in the controller
if you are in an older version of ASP.NET you can use this older command
#Html.Partial("_yourPartial")
it works the same way, and avoid using jquery
you can also pass a model through it, using the command
#Html.Partial("_yourPartial", new { paramName = "foo" })

Server side paging & sorting and filtering Performance

I have only one table in database with 2 million records , i want the user to be able to browse the data and also have the ability to sort data and filter it.
also user should be able to navigate between pages
Here is my MVC controller
public class AssetController : Controller
{
private ApplicationDbContext _dbContext;
public ApplicationDbContext DbContext
{
get
{
return _dbContext ?? HttpContext.GetOwinContext().Get<ApplicationDbContext>();
}
private set
{
_dbContext = value;
}
}
public AssetController()
{
}
public AssetController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
// GET: Asset
public ActionResult Index()
{
return View();
}
public ActionResult Get([ModelBinder(typeof(DataTablesBinder))] IDataTablesRequest requestModel)
{
IEnumerable<Asset> query = DbContext.Assets;
var totalCount = query.Count();
#region Filtering
// Apply filters for searching
if (requestModel.Search.Value != string.Empty)
{
var value = requestModel.Search.Value.Trim();
query = query.Where(p => p.Barcode.Contains(value) ||
p.Manufacturer.Contains(value) ||
p.ModelNumber.Contains(value) ||
p.Building.Contains(value)
);
}
var filteredCount = query.Count();
#endregion Filtering
#region Sorting
// Sorting
var sortedColumns = requestModel.Columns.GetSortedColumns();
var orderByString = String.Empty;
foreach (var column in sortedColumns)
{
orderByString += orderByString != String.Empty ? "," : "";
orderByString += (column.Data) + (column.SortDirection == Column.OrderDirection.Ascendant ? " asc" : " desc");
}
query = query.OrderBy(orderByString == string.Empty ? "BarCode asc" : orderByString);
#endregion Sorting
// Paging
query = query.Skip(requestModel.Start).Take(requestModel.Length);
var data = query.Select(asset => new
{
AssetID = asset.AssetID,
BarCode = asset.Barcode,
Manufacturer = asset.Manufacturer,
ModelNumber = asset.ModelNumber,
Building = asset.Building,
RoomNo = asset.RoomNo,
Quantity = asset.Quantity
}).ToList();
return Json(new DataTablesResponse(requestModel.Draw, data, filteredCount, totalCount), JsonRequestBehavior.AllowGet);
}
}
and below is index.cshtml
<div class="row">
<div class="col-md-12">
<div class="panel panel-primary list-panel" id="list-panel">
<div class="panel-heading list-panel-heading">
<h1 class="panel-title list-panel-title">Properties</h1>
</div>
<div class="panel-body">
<table id="datatable" class="table table-striped table-bordered" style="width:100%;">
<thead>
<tr>
<th>BarCode</th>
<th>Manufacturer</th>
<th>Building</th>
<th>Quantity</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
#section Scripts
{
<script type="text/javascript">
$(document).ready(function () {
var datatableInstance = $('#datatable').DataTable({
serverSide: true,
processing: true,
"ajax": {
"url": "#Url.Action("Get","Asset")"
},
lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
columns: [
{ 'data': 'BarCode' },
{ 'data': 'Manufacturer' },
{
'data': 'Building',
'searchable': true,
},
{
'data': 'Quantity',
'searchable': true,
'render': function (Quantity) {
return "$ " + Quantity;
}
},
],
});
});
</script>
}
I used JQuery data-tables , code is working fine if i have small number of rows - less than 100000 , but if i have large number of rows , it becomes very bad
It takes too long in the following line
var filteredCount = query.Count();
How can i enhance the performance
In my opinion, instead doing "var totalCount = query.Count();" try to make an sql statement with a count over the table, probably you will get a better perfomance.
var totalCount = _context.SqlQuery("Select count(0) from yourTable").FirstOrDefault();
I havenĀ“t tested the perfomance with Count(), but with Distinc() the difference is remarkable.
Hope it helps

better way to load 2 dropdown in mvc

This is how i am loading on page load state and city dropdown:
My Controller method:
This is the first method which is calling when page is loaded.
public ActionResult Index()
{
var states = GetStates();
var cities = Enumerable.Empty<SelectListItem>();
ViewBag.States = states;
ViewBag.Cities = cities;
}
private IEnumerable<SelectListItem> GetStates()
{
using (var db = new DataEntities())
{
return db.States.Select(d => new SelectListItem { Text = d.StateName, Value =d.Id.ToString() });
}
}
[HttpGet]
public ActionResult GetCities(int id)
{
using (var db = new DataEntities())
{
var data = db.Cities.Where(d=>d.StateId==id).Select(d => new { Text = d.CityName, Value = d.Id }).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
}
My View:
IEnumerable<SelectListItem> States = ViewBag.States;
IEnumerable<SelectListItem> Cities = ViewBag.Cities;
#Html.DropDownList("State", States, "Select State", new { onchange="loadCities(this)"})
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity"})
function loadCities(obj) {
$.ajax({
url: "/Home/GetCities",
data: { id: $(obj).val() },
contentType:"application/json",
success:function(responce){
var html = '<option value="0">Select City</option>';
$(responce).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
}
});
}
Any better way then this to load state and city dropdown?
public class HomeController : Controller
{
public ActionResult Index(int id=0)
{
Person model = null;
var states = GetStates().ToList();
var cities = Enumerable.Empty<SelectListItem>();
if (id > 0)
{
using (var db = new DataEntities())
{
model = db.People.Include("City").FirstOrDefault(d => d.Id == id);
if (model == null)
model = new Person();
else
{
states.First(d => d.Value == model.City.StateId.ToString()).Selected = true;
cities = db.Cities.Where(d => d.StateId == model.City.StateId).ToList().Select(d => new SelectListItem { Text = d.CityName,Value=d.Id.ToString(),Selected=d.Id==model.CityId });
}
}
}
else
{
model = new Person();
}
ViewBag.States = states;
ViewBag.Cities = cities;
ViewBag.Persons = GetPersons();
return View(model);
}
[HttpGet]
public ActionResult GetCities(int id)
{
using (var db = new DataEntities())
{
var data = db.Cities.Where(d=>d.StateId==id).Select(d => new { Text = d.CityName, Value = d.Id }).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}
}
public ActionResult SavePersonDetail([Bind(Exclude = "Id")] Person model)
{
// var employeeDal= new Emploee();
//employee.firstname=model.
if (ModelState.IsValid)
{
var Id = model.Id;
int.TryParse(Request["Id"], out Id);
using (var db = new DataEntities())
{
if (Id > 0)
{
var person = db.People.FirstOrDefault(d => d.Id == Id);
if (person != null)
{
model.Id = Id;
db.People.ApplyCurrentValues(model);
}
}
else
{
db.People.AddObject(model);
}
db.SaveChanges();
}
}
if (!Request.IsAjaxRequest())
{
ViewBag.States = GetStates();
ViewBag.Persons = GetPersons();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
return View("Index");
}
else
{
return PartialView("_personDetail",GetPersons());
}
}
public ActionResult Delete(int id)
{
using (var db = new DataEntities())
{
var model = db.People.FirstOrDefault(d => d.Id == id);
if (model != null)
{
db.People.DeleteObject(model);
db.SaveChanges();
}
}
if (Request.IsAjaxRequest())
{
return Content(id.ToString());
}
else
{
ViewBag.States = GetStates();
ViewBag.Persons = GetPersons();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
return View("Index");
}
}
private IEnumerable<SelectListItem> GetStates()
{
using (var db = new DataEntities())
{
return db.States.ToList().Select(d => new SelectListItem { Text = d.StateName, Value =d.Id.ToString() });
}
}
private IEnumerable<Person> GetPersons()
{
using (var db = new DataEntities())
{
return db.People.Include("City").Include("City.State").ToList();
}
}
public ActionResult HomeAjax()
{
ViewBag.States = GetStates();
ViewBag.Cities = Enumerable.Empty<SelectListItem>();
using (var db = new DataEntities())
{
var data = db.States.Include("Cities").Select(d => new { Id = d.Id, Name = d.StateName, Cities = d.Cities.Select(x => new { Id=x.Id,Name=x.CityName}) }).ToList();
ViewBag.CityStateJson = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(data);
}
ViewBag.Persons = GetPersons();
return View();
}
}
#model IEnumerable<Person>
<div>
<table>
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Email
</th>
<th>
City
</th>
<th>
State
</th>
<th>
Edit
</th>
</tr>
#if (Model.Count() == 0)
{
<tr>
<td colspan="6">
<h3>No data available</h3>
</td>
</tr>
}
else {
foreach (var item in Model) {
<tr data-id="#item.Id">
<td data-id="fn">#item.FirstName</td>
<td data-id="ln">#item.LastName</td>
<td data-id="email">#item.Email</td>
<td data-id="cn">#item.CityName<input type="hidden" value="#item.CityId" /></td>
<td>#item.StateName</td>
<td>
#if (ViewBag.Title == "Home Ajax" || Request.IsAjaxRequest())
{
Update
<span>#Ajax.ActionLink("Delete", "Delete", new { id = item.Id }, new AjaxOptions {OnSuccess="deleteSuccess",OnBegin="showLoader",OnComplete="hideLoader" })</span>
}
else {
<span>#Html.ActionLink("Update", "Index", new { id = item.Id })</span>
<span>#Html.ActionLink("Delete", "Delete", new { id = item.Id })</span>
}
</td>
</tr>
}
}
</table>
</div>
#model Person
#{
ViewBag.Title = "Home Ajax";
IEnumerable<Person> persons = ViewBag.Persons;
IEnumerable<SelectListItem> States = ViewBag.States;
IEnumerable<SelectListItem> Cities = ViewBag.Cities;
IEnumerable<State> fullStates=ViewBag.CityStates;
}
#section featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
</div>
</section>
}
#section styles{
<style type="text/css">
td,th {
border:1px solid;
padding:5px 10px;
}
select {
padding:5px 2px;
width:310px;
font-size:16px;
}
</style>
}
#section scripts{
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
var jsonArray = #Html.Raw(ViewBag.CityStateJson)
function clearValues() {
$("input[type='text'],select").val('');
$("input[type='hidden'][name='Id']").val(0);
}
function loadCities(obj) {
for (var i = 0; i < jsonArray.length; i++) {
if (jsonArray[i].Id == parseInt($(obj).val())) {
fillCity(jsonArray[i].Cities);
break;
}
}
}
function Edit(obj, Id) {
// alert("hi")
$("input[type='hidden'][name='Id']").val(Id);
var tr = $(obj).closest("tr");
$("#txtfirstName").val($("td[data-id='fn']", tr).text().trim());
$("#txtlastName").val($("td[data-id='ln']", tr).text().trim());
$("#txtemail").val($("td[data-id='email']", tr).text().trim());
var city = $("td[data-id='cn'] input[type='hidden']", tr).val();
var state;
for (var i = 0; i < jsonArray.length; i++) {
for (var j = 0; j < jsonArray[i].Cities.length; j++) {
if (jsonArray[i].Cities[j].Id == parseInt(city)) {
state = jsonArray[i].Id;
break;
}
}
if (state) {
fillCity(jsonArray[i].Cities);
break;
}
}
$("#ddlState").val(state);
$("#ddlCity").val(city);
}
function fillCity(obj) {
var html = '<option value="0">Select City</option>';
$(obj).each(function () {
html += '<option value="' + this.Id + '">' + this.Name + '</option>'
});
$("#ddlCity").html(html);
}
function deleteSuccess(responce) {
alert("record deleted successfully");
$("tr[data-id='" + responce + "']").remove();
}
function insertSuccess() {
alert("Record saved successfully");
clearValues();
}
function showLoader() {
$("#overlay").show();
}
function hideLoader() {
$("#overlay").hide();
}
</script>
}
<h3>Add Personal Detail</h3>
#using (Ajax.BeginForm("SavePersonDetail", "Home", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "personList" ,OnSuccess="insertSuccess",OnBegin="showLoader",OnComplete="hideLoader"}))
{
#Html.HiddenFor(m => m.Id);
<ol class="round">
<li>
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName, new { id = "txtfirstName" })
#Html.ValidationMessageFor(m => m.FirstName)
</li>
<li>
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName, new { id = "txtlastName" })
#Html.ValidationMessageFor(m => m.LastName)
</li>
<li>
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email, new { id = "txtemail" })
#Html.ValidationMessageFor(m => m.Email)
</li>
<li>
#Html.Label("State")
#Html.DropDownList("State", States, "Select State", new { onchange = "loadCities(this)", id = "ddlState" })
</li>
<li>
#Html.LabelFor(m => m.CityId)
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id = "ddlCity" })
#Html.ValidationMessageFor(m => m.CityId)
</li>
</ol>
<input type="submit" value="Save" />
<input type="button" value="Cancel" onclick="clearValues();"/>
}
<h2>
Person List
</h2>
<div style="position:fixed;text-align:center;top:0;bottom:0;left:0;right:0;z-index:10;background-color:black;opacity:0.6;display:none;" id="overlay">
<img style="position:relative;top:370px" src="~/Images/ajax-loader.gif" />
</div>
<div id="personList">
#Html.Partial("_personDetail", persons)
</div>
You approach using ajax is fine although I would recommend a few better practices including using a view model with properties for StateID, CityID StateList and CityList, and using Unobtrusive JavaScript rather than polluting you markup with behavior, and generating the first ("please select") option with a null value rather than 0 so it can be used with the [Required] attribute
HTML
#Html.DropDownList(m => m.StateID, States, "Select State") // remove the onchange
#Html.DropDownListFor(m => m.CityID, Cities, "Select City") // why change the default ID?
SCRIPT
var url = '#Url.Action("GetCities", "Home")'; // use the helper (dont hard code)
var cities = $('#CityID'); // cache the element
$('#StateID').change(function() {
$.getJSON(url, { id: $(this).val() }, function(response) {
// clear and add default (null) option
cities.empty().append($('<option></option>').val('').text('Please select'));
$.each(response, function(index, item) {
cities.append($('<option></option>').val(item.Value).text(item.Text));
});
});
});
If you were rendering multiple items (say you were asking the user to select their last 10 cities they visited), you can cache the result of the first call to avoid repeated calls where their selections may include cities from the same state.
var cache = {};
$('#StateID').change(function() {
var selectedState = $(this).val();
if (cache[selectedState]) {
// render the options from the cache
} else {
$.getJSON(url, { id: selectedState }, function(response) {
// add to cache
cache[selectedState] = response;
.....
});
}
});
Finally, in response to your comments regarding doing it without ajax, you can pass all the cities to the view and assign them to a javascript array. I would only recommend this if you have a few countries, each with a few cities. Its a matter of balancing the slight extra initial load time vs the slight delay in making the ajax call.
In the controller
model.CityList = db.Cities.Select(d => new { City = d.CountryID, Text = d.CityName, Value = d.Id }).ToList();
In the view (script)
// assign all cities to javascript array
var allCities= JSON.parse('#Html.Raw(Json.Encode(Model.CityList))');
$('#StateID').change(function() {
var selectedState = $(this).val();
var cities = $.grep(allCities, function(item, index) {
return item.CountryID == selectedState;
});
// build options based on value of cities
});
This is a correct approach, but you can simplify your javascript:
function loadCities(obj) {
$.getJSON("/Home/GetCities", function (data) {
var html = '<option value="0">Select City</option>';
$(data).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
});
}
Further possible simplification:
Add the default item (Select City) server-side, so your javascript will be smaller.
Here's how I'd do it without the page refresh, assuming the list of cities isn't too long.
I'm assuming you can create a GetStatesAndCities method to return a Dictionary.
public ActionResult Index()
{
Dictionary<string, List<String>> statesAndCities = GetStatesAndCities();
ViewBag.StatesAndCities = Json(statesAndCities);
}
Then in the view:
var states = JSON.parse(#ViewBag.StatesAndCities);
function loadCities(obj) {
var cities = states[$(obj).val()];
var html = '<option value="0">Select City</option>';
$(cities).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'
});
$("#ddlCity").html(html);
}
This way when the state is changed the cities field with update immediately with no need for callback.
disclaimer: This is not a code answer, there are plenty other answers.
I think best way to keep yourself happy to seperate UI pages from data => turn them into API calls:
/GetCities
/GetStates
Now you can simply leave the select's empty on Razor rendering the page. And use a Jquery/Bootstrap plugin to create an AJAX select box.
This way when the user stops typing his search, this search string can than be send with the AJAX call (eg: /GetStates?search=test) and then a small result set can be send back to the website.
This gives:
Better separation in serveside code
Better User eXperience.
Smaller page loads (since you no longer send all the options to user when he requests the page, only when he opens the select box).
How about using Knockout?
Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model
You have to use ajax for your cities. But with knockout you dont need to write
var html = '<option value="0">Select City</option>';
$(responce).each(function () {
html += '<option value="'+this.Value+'">'+this.Text+'</option>'});
$("#ddlCity").html(html);
in your javascript.Knockout makes it simple.
You can simply write:
function CityModel() {
var self = this; // that means this CityModel
self.cities = ko.observableArray([]);
self.getCities = function () {
$.ajax({
url: "/Home/GetCities",
data: { id: $(obj).val() },
contentType: "application/json",
success: self.cities
});
}
}
ko.applyBindings(new CityModel());
thats all. But you have to bind your data into html elements.
Instead of using :
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity"})
You can use:
<select data-bind="options:cities,optionsValue:"Id",optionsText:"CityName",optionsCaption:"Select City""></select>
or you can mix razor and knockout:
#Html.DropDownListFor(m => m.CityId, Cities, "Select City", new { id="ddlCity",data_bind:"options:cities,optionsValue:\"Id\",optionsText:\"CityName\""})
One more thing you have to call GetCities when State changes, you can :
#Html.DropDownList("State", States, "Select State", new {data_bind:"event:\"change\":\"$root.GetCities\""})
Dont be scare with \"\" things this because " is an escape character and we have to say to razor i want to use " by using \ before it.
You can find more info about knockout :Knockout
And mixing with razor: Razor and Knockout
Ps: yes using knockout is suspend us from Razor and Mvc. You have to write another ViewModel . But like this situations ko is helpful. Mixing razor and knockout is another option for you.

Table not refreshing using ajax

I an experimenting with MVC. I have a view which contains a dropdownlist and a table.
When I select an option from the dropdownlist, I want to get the data from my controller and update the view:
View:
<div>
<h2>Current Print Statistics</h2>
#using (Html.BeginForm())
{
#Html.DropDownList("LogTypes", new SelectList(Model.LogTypes, "Value", "Text"), new
{
id = "logType",
data_url = Url.Action("GetStatistics", "Home")
})
<table id="modelTable" class="table table-condensed">
<tr>
<th>RequestedOn</th>
<th>PrintedOn</th>
<th>Message</th>
<th>Success</th>
<th>TemplateName</th>
</tr>
<tbody>
#foreach (var item in Model.PrintLogs)
{
string css = (item.Success) ? "success" : "danger";
string link = (item.Success) ? "www.google.com" : string.Empty;
<tr class="#css">
<td>#item.RequestedOn</td>
<td>#item.PrintedOn</td>
<td>#item.Message</td>
#if (item.Success)
{
<td>#item.Success</td>
}
else
{
<td>#Html.ActionLink("False", "Index", "LogView", new { id = item.LogID }, null)</td>
}
<td>#item.TemplateName</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script type="text/javascript">
$(function () {
$('#logType').change(function () {
console.log($(this).data('url'));
var selectedValue = $(this).val();
var table = $('#modelTable');
$.ajax({
url: $(this).data('url'),
type: 'GET',
cache: false,
context: table,
data: { value: selectedValue },
success: function (result) {
$.each(result.PrintLogs,
function (index, log) {
$('<tr/>', {
html: $('<td/>', {
html: log.RequestedOn
}).after($('<td/>', {
html: log.PrintedOn
})).after($('<td/>', {
html: log.Success
})).after($('<td/>', {
html: log.Message
})).after($('<td/>', {
html: log.TemplateName
}))
}).appendTo(tableBody);
}
);
}
});
});
});
</script>
Controller:
[HttpGet]
public JsonResult GetStatistics(string value)
{
var request = LogTypeRequest.Last24H;
if (value == "0") request = LogTypeRequest.Last24H;
if (value == "1") request = LogTypeRequest.LastWeek;
if (value == "2") request = LogTypeRequest.LastMonth;
var model = new PrintServerModel
{
LogTypes = new List<ListItem>
{
new ListItem() {Text = "Last 24 Hours", Value = "0"},
new ListItem() {Text = "Last Week", Value = "1"},
new ListItem() {Text = "Last Month", Value = "2"}
},
PrintLogs = PrintServerService.GetPrinterLog(request)
};
return Json(model, JsonRequestBehavior.AllowGet);
}
Now when I try to debug in chrome, when the line $.ajax({ is reached it seems to jump to the end.
Ideally what I want is to display the data on start up and then when the user selects something from the dropdown, refresh the data.
Any help greafully appreciated!!
Odds are there is an error from the json call, you should add .done or error: to the end of it so you can see what your error is.
also there is a tab in chrome debug tool for watching the network calls, you may be able to see the response from the call in there with some additional details.
just looking over the ajax call, may want to change to have
data: JSON.stringify( { value: selectedValue }),
if you get more info i will do what i can to better my answer.

how can I show the user that the data is gone when he trying to edit a field?

so when the user click the edit link to edit one of the client (field) and another user have erase already that client how can show to the user that the client (field) is gone?
so I'am using TempData is another way to do it? i think jquery but i don't know how to use it properly
public ActionResult Edit (int id)
{
client cliente = db.Clients.Find(id);
if (cliente != null)
{
return View(cliente);
}
TempData["message"] = string.Format("this client have be erase for other user");
return RedirectToAction("Index");
}
edit:
and view is this
<table class="widgets">
<tr>
<th></th>
<th>
#Html.ActionLink("Nombre", "Index", new { ordenacion = ViewBag.NameSortParm, filtro = ViewBag.filtro })
</th>
</tr>
#foreach (var item in Model) {
<tr id="widget-id-#item.id">
<td>
#Html.ActionLink("Editar", "Edit", new { id=item.id }) |
#Ajax.ActionLink("Eliminar", "Eliminar", "Cliente",
new {item.id },
new AjaxOptions {
HttpMethod = "POST",
Confirm = string.Format("Esta Seguro que quiere eliminar '{0}'?", item.descripcion),
OnSuccess = "deleteConfirmation"
})
</td>
<td>
#Html.DisplayFor(modelItem => item.descripcion)
</td>
</tr>
}
</table>
i guees the script will be this? so i have to make my edit link like i did for the delete (eliminar) link using #Ajax.ActionLink right?
<script type="text/javascript">
var validateForEdit = function (id) {
var validateCallbackFunction = function (result) {
if (result) {
window.location = '/Client/Editar/' + id;
}
else {
window.Alert('this client have be erase for other user');
}
};
$.post('/Client/ValidateForEdit/', { id: id }, validateCallbackFunction, 'json');
}
</script>
Hi you can use following code to validate data before user can edit that
var validateForEdit = function (id) {
var validateCallbackFunction = function (result) {
if (result) {
window.location = '/Client/Edit/' + id;
}
else {
Alert('this client have be erase for other user');
}
};
$.post('/Client/ValidateForEdit/', { id: id }, validateCallbackFunction, 'json');
}
And your Action :
[HttpPost]
public JsonResult ValidateForEdit(int id)
{
var cliente = db.Clients.Find(id);
return cliente != null ? Json(true) : Json(false);
}
Edit : And you have to replace your following code
#Html.ActionLink("Editar", "Edit", new { id=item.id })
with this code :
<input class="button" type="button" value="Edit" onclick="validateForEdit(item.id)" />
Hope this help.

Categories

Resources