MVC3 Ajax Ajax.ActionLink in Views Shared _Layout.cshtml - ajax

I have an mvc3 web app I used a shared view under
Views
Shared_Layout.cshtml
_Layout.cshtml contains markups which acts like a master page for our app
#using .............
#using ......
<!DOCTYPE html>
<html style="overflow: hidden">
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.min.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/themes/metro-light/jquery-ui-1.8.16.custom.css")" rel="stylesheet" type="text/css" />
#RenderSection("CSS", false)
<script src="#Url.Content("~/Scripts/jquery-1.6.2.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.16.custom.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"> </script>
...... (some other scripts here)
......
</head>
<body style="height: 100%">
<!-- page -->
<div class="page">
<div id="header">
<div id="title">
<img alt="" src="#Url.Content("~/Content/images/Logo.png")"/>#ViewBag.ApplicationName
</div>
<div id="logindisplay">
Welcome <strong>#User.Identity.Name</strong>
</div>
<ul id="menu">
<li>#Ajax.ActionLink("Dashboard", "Index","Dashboard", new {#class = "Map View".Equals(ViewBag.Title) ? "selected" : null},new AjaxOptions { UpdateTargetId = "main", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" })</li>
<li>#Ajax.ActionLink("Map View", "Index", "Map", new { #class = "Map View".Equals(ViewBag.Title) ? "selected" : null },new AjaxOptions { UpdateTargetId = "main", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" })</li>
<li>#Ajax.ActionLink("Settings", "Index", "Settings", new { #class = "Settings".Equals(ViewBag.Title) ? "selected" : null }, new AjaxOptions { UpdateTargetId = "main", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" })</li>
<li>#Ajax.ActionLink("About", "Index", "AppInfo", new { #class = "About".Equals(ViewBag.Title) ? "selected" : null }, new AjaxOptions { UpdateTargetId = "main", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" })</li>
</ul>
</div>
<div id="main">
#RenderBody()
</div>
</div>
<!-- footer -->
<div id="footer">
</div>
<form id="__ajaxAntiForgeryForm" action="#" method="post">#Html.AntiForgeryToken()</form>
</body>
</html>
Basically, from the above code I am replacing my "main" div with whatever the controller return
new AjaxOptions { UpdateTargetId = "main", InsertionMode = InsertionMode.Replace, HttpMethod = "GET" }
Here are my controllers (nothing special)
DashboardController
[UiException]
public ActionResult Index()
{
return View();
}
MapController
[UiExceptionAttribute]
public ActionResult Index()
{
return View();
}
When I load the page and click on the links "Dashboard" or "Map View", the whole page flickers and refresh. Supposed to be #Ajax.ActionLink does not refresh the whole page but instead just the "main" div. Is there a way to have an ajax like behavior refreshing only the content section? Can anybody help? Thanks!

it is because you are returning the whole view from controller code which includes the whole masterpage(i.e. ur layout).
You need to render only partial view of your dashboard or map.
like:
DashboardController
[UiException]
public ActionResult Index()
{
return PartialView("yourviewname",model)
}
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}

Your unobtrusive Javascript include is missing so the action links are actually no "real" ajax links because no event handler have been attached to it (the unobtrusive-ajax script basically looks for all links with attribute data-ajax=true and attaches methods to it to do the ajax stuff).
So include the jquery.unobtrusive-ajax.min.js file as well and it should work as expected.

as above i found this useful too.. without putting the View on the _Shared folder.
/// <summary>
/// Dynamically Render a View using the passed-in context
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="viewName"></param>
/// <param name="model"></param>
/// <returns></returns>
public static string RenderView(ControllerContext controllerContext, string viewName, object model)
{
controllerContext.Controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controllerContext, viewName);
var viewContext = new ViewContext(controllerContext, viewResult.View, controllerContext.Controller.ViewData, controllerContext.Controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}

Related

asp.net mvc + Ajax.BeginForm OnBegin not firing

After reading several posts regarding this issue, it is mostly related to including
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
and the proper order of including jquery first.
<script src="~/Scripts/jquery-1.12.4.js"></script>
I think I did properly, but OnBegin is not firing still.
_Layout.cshtml:
<head>
<meta charset="utf-8" />
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<title>Test</title>
#Scripts.Render("~/bundles/modernizr")
<script src="~/Scripts/jquery-1.12.4.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/respond.min.js"></script>
<script src="~/Scripts/jquery.dataTables.js"></script>
<script src="https://cdn.datatables.net/responsive/2.1.0/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.2/js/buttons.colVis.min.js"></script>
<script src="~/Scripts/light-bootstrap-dashboard.js"></script>
<script src="~/Scripts/bootstrap-notify.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
#RenderSection("scripts", required: false)
</head>
So the order of process is:
School View:
#model Test.Models.School
<div class="editorRow">
#using (Html.BeginCollectionItem("schools"))
{
#Html.ActionLink("New Employee", "NewEmployee", "Test", new
{
#class = "btn btn-info btn-md",
data_toggle = "modal",
data_target = "#modal-container-employee"
})
</div>
<div id="modal-container-employee" class="modal fade" tableindex="-1" role="dialog">
<div class="modal-content">
<div class="modal-body">
</div>
</div>
</div>
Controller:
public ActionResult NewEmployee()
{
var newEmployee = new Employee();
return View(newEmployee);
}
Employee view:
#model Test.Models.Employee
#{
Layout = null;
}
#using (Ajax.BeginForm("PostEmployee", "Employee", new AjaxOptions
{
HttpMethod = "post",
InsertionMode = InsertionMode.Replace,
OnBegin = "closeModal"
}))
{
#Html.AntiForgeryToken()
<div id="test" class="">
<div class="modal-title title">
<input type="submit" id="submit" value="Save" /><span id="closes">x</span>
</div>
#Html.EditorFor(model => model.FullName)
</div>
}
<script>
function closeModal() {
$('#modal-container').modal('hide');
}
</script>
controller:
[HttpPost]
public ActionResult PostEmployee(Employee model)
{
try
{
var newEmployee = new Employee
{
FullName = model.FullName
};
db.Employees.Add(newEmployee);
db.SaveChanges();
return Json(new { status = "success" }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { status = "error", errorMessage = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
As a result, "success" Json is returned, but ajax is not catching it, it directly goes to the other page where it shows JSON array {"status":"success"}.
Can anyone find the problem?

Ajax.Beginform refreshing whole page

It redirects to whole new page instead of just updating the current one, I have unobtrusive scripts and web.config stuff. Why is this not working?
<div id="preview-image-placeholder"></div>
#foreach (var item in Model)
{
using (Ajax.BeginForm("PreviewImage/" + #item.Id.ToString(), "Music", null, new AjaxOptions { HttpMethod = "Post", InsertionMode = InsertionMode.Replace, UpdateTargetId = "preview-image-placeholder" },
new { #enctype = "multipart/form-data", #item.Id }))
{
#Html.AntiForgeryToken()
<input type="file" class="upload-image" id="upload-image-file_#item.Id" name="file" accept="image/*">
<button class="btn btn-default" id="btn-preview_#item.Id" style="display: none" type="submit"></button>
}
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
Controller:
[ValidateAntiForgeryToken]
[HttpPost]
public PartialViewResult PreviewImage(HttpPostedFileBase file, int id)
{
//Do other code stuff
return PartialView("_DisplayImage", song);
}
_DisplayImage partial view:
#model IEnumerable<MusicSite.Models.UploadedSong>
#using MusicSite.CustomHtmlHelpers;
#foreach(var item in Model)
{
if (#item.CoverImageBytes != null)
{
#Html.ImageFor(x => item.CoverImageBytes, #item.SongName + " Image", new { #id = "preview-image" })
}
}
Besides unobtrusive scripts you must add Microsoft Ajax libraries too (MicrosoftAjax.js and MicrosoftMvcAjax.js) so that Ajax.BeginForm works correctly.
Regards,

ajax. The resource cannot be found

I'm using C# asp.net mvc4 and trying to do ajax search. But ther is error and it says " The resource cannot be found.".
What I'm doing wrong?
Controller
//Search
[HttpPost]
public ActionResult ContractSearch(string Name)
{
var contracts = db.Contracts.Include(c => c.DocType).Include(c => c.CreditType).Include(c => c.Bank).Include(c => c.UserProfile).Where(c => c.FirstName.Equals(Name));
return View(contracts.ToList());
}
View
#model IEnumerable<CreditoriyaApp.Models.Contract>
#{
ViewBag.Title = "Index";
}
<div>
#using (Ajax.BeginForm("ContractSearch", "Contract", new AjaxOptions { UpdateTargetId = "searchresults" }))
{
<input type="text" name="Name" />
<input type="submit" value="Search" />
}
<div id="searchresults">
#if (Model != null && Model.Count()>0)
{
<ul>
#foreach (var item in Model)
{
<li>#item.FirstName</li>
}
</ul>
}
</div>
Error
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Contract/ContractSearch
Add the below in your controller. Then your error will be corrected.
public ActionResult ContractSearch()
{
return View();
}
for searching you can try something like below example.
Model:
public class Person
{
public string Name { get; set; }
public string Country { get; set; }
}
Controller:
public ActionResult SearchPerson()
{
return View();
}
[HttpPost]
public ActionResult SearchPerson(string searchString)
{
System.Collections.Generic.List<Person> lst = new List<Person>();
lst.Add(new Person { Name = "chamara", Country = "Sri Lanka" });
lst.Add(new Person { Name = "Arun", Country = "India" });
if (!string.IsNullOrEmpty(searchString))
{
lst = lst.AsEnumerable().Where(r => r.Name.Contains(searchString)).ToList();
}
string result = string.Empty;
result = "<p>Search Result<p>";
foreach (Person item in lst)
{
result = result + "<p> Names is: " + item.Name + " and Country is:" + item.Country + "<p>";
}
return Content(result, "text/html");
}
View:
#model IEnumerable<Mvc4Test.Models.Person>
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>SearchPerson</title>
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
</head>
<body>
#using (Ajax.BeginForm("SearchPerson", "Search", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "searchresults" }))
{
#Html.TextBox("searchString")
<input type="submit" value="Search" />
}
<div id="searchresults">
</div>
</body>
</html>

How to hide the webgrid when execute the page in mvc3 razor?

When I remove the webgrid and layout=null in the view, its executing and client side validation message is displaying... but When I execute the page with layout= nlll and Webgrid it displaying the below error "A data source must be bound before this operation can be performed"
Controller :
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Display()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Display(string brandname)
{
ShoppingClass s = new ShoppingClass();
var ob= s.Searching(brandname);
return View(ob);
}
view :
#model List<ShoppingCart.Models.ShoppingClass>
#{
ViewBag.Title = "Display";
Layout = null;
}
#{
var grid = new WebGrid(source: Model, defaultSort: "Drug_Code", rowsPerPage: 20);
}
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm(null, null, FormMethod.Post))
{
for (int i = 0; i < 1; i++)
{
#*
<table><tr><td> #Html.Label("BrandName")</td>
<td>#Html.TextBox("BrandName")<div>#Html.ValidationMessage("BrandName")</div></td>
<td><input type="submit" value="Search" name="Search" /></td></tr></table>*#
<table><tr><td> #Html.LabelFor(o => o[i].BrandName)</td>
<td>#Html.TextBoxFor(o => o[i].BrandName)<div>#Html.ValidationMessageFor(o => o[i].BrandName)</div></td>
<td><input type="submit" value="Search" name="Search" /></td></tr></table>
}
}
<div id="grid">
#grid.GetHtml(tableStyle: "listing-border", headerStyle: "gridhead", footerStyle: "paging", rowStyle: "td-dark", alternatingRowStyle: "td-light",
columns: grid.Columns(
grid.Column("GenericName", format: #<text>#item.GenericName</text>),
grid.Column("BrandName", format: #<text>#item.BrandName</text>),
grid.Column("Purchaseqty", format: #<text>#item.Purchaseqty</text>),
grid.Column("Purchaseprice", format: #<text>#item.Purchaseprice</text>),
grid.Column("Drug_Code", format: #<text>#item.Drug_Code</text>),
grid.Column(header: "", format: (item) => Ajax.ActionLink("Add to Cart", "ADDTOCART",
new { brandname = #item.BrandName, purchaseqty = #item.Purchaseqty, drugcode = #item.Drug_Code }, new AjaxOptions { HttpMethod = "Post", OnSuccess = "ADDTOCART" }))
)
)
</div>
If you want to hide the grid if the model is null, then you can put an if statement in that checks if the model is null... ie.
if (model != null)
{
// Make and populate grid here
}

MVC3 Ajax.BeginForm with javascript disabled

I'm having a problem getting a form to work without javascript being enabled.
This should be enough to go on, ask if you need to know anything else - I don't want to just put the whole solution up here!
~/Views/_ViewStart.cshtml:
#{ Layout = "~/Views/Shared/Layout.cshtml"; }
~/Views/Shared/Layout.cshtml:
#using System.Globalization; #{ CultureInfo culture = CultureInfo.GetCultureInfo(UICulture); }<!DOCTYPE html>
<html lang="#culture.Name" dir="#(culture.TextInfo.IsRightToLeft ? "rtl" : "ltr")">
<head>
<title>AppName :: #ViewBag.Title</title>
<link href="#Url.Content("~/favicon.ico")" rel="shortcut icon" type="image/x-icon" />
<link href="#Url.Content("~/apple-touch-icon.png")" rel="apple-touch-icon" />
<link href="#Url.Content("~/Content/css/site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Content/js/jquery-1.6.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/app.js")" type="text/javascript"></script>
#RenderSection("SectionHead", false)
</head>
<body>
<div id="page-container">
<div id="nav">
<div id="nav-user">
#{ Html.RenderAction("LoginStatus", "Account"); }
#{ Html.RenderPartial("CultureSelector"); }
</div>
</div>
<div id="page-content">
<h2>#ViewBag.Title</h2>
#RenderBody()
</div>
</div>
</body>
</html>
~/Views/Account/Index.cshtml:
#model AccountFilterModel
#{
ViewBag.Title = "Account Home";
var loadingId = "loading" + new Random().Next();
Model.FilterFormId = "filter-account-form";
}
#using (Ajax.BeginForm("List", "Account", Model, new AjaxOptions { UpdateTargetId = "result-list", LoadingElementId = loadingId }, new { id = "filter-account-form" })) {
<!-- form controls and validation summary stuff -->
<input id="filter" type="submit" value="Filter" />
<span id="#loadingId" style="display: none">
<img src="#Url.Content("~/Content/images/ajax-loader.gif")" alt="Loading..." />
</span>
}
<div id="result-list">
#{ Html.RenderAction("List", Model); }
</div>
~/Views/Account/List.cshtml:
#model FilterResultModel
#helper SortLink(AccountSort sort, SortDirection dir) {
string display = (dir == SortDirection.Ascending ? "a" : "d"); // TODO: css here
if (Model.Filter.SortBy != null && ((AccountSortModel)Model.Filter.SortBy).Sort == sort && dir == Model.Filter.SortOrder) {
#:#display
} else {
FilterModel fm = new FilterModel(Model.Filter);
fm.SortBy = AccountSortModel.SortOption[sort];
fm.SortOrder = dir;
#display
}
}
#if (Model.Results.Count > 0) {
var first = Model.Results.First();
<table>
<caption>
#string.Format(LocalText.FilterStats, Model.FirstResultIndex + 1, Model.LastResultIndex + 1, Model.CurrentPageIndex + 1, Model.LastPageIndex + 1, Model.FilteredCount, Model.TotalCount)
</caption>
<thead>
<tr>
<th>
#Html.LabelFor(m => first.Username)
<span class="sort-ascending">
#SortLink(AccountSort.UsernameLower, SortDirection.Ascending)
</span>
<span class="sort-descending">
#SortLink(AccountSort.UsernameLower, SortDirection.Descending)
</span>
</th>
<!-- other table headers -->
</tr>
</thead>
<tbody>
#foreach (AccountModel account in Model.Results) {
<tr>
<td>#Html.EncodedReplace(account.Username, Model.Filter.Search, "<span class=\"filter-match\">{0}</span>")</td>
<!-- other columns -->
</tr>
}
</tbody>
</table>
Html.RenderPartial("ListPager", Model);
} else {
<p>No Results</p>
}
Relevant part of AccountController.cs:
public ActionResult Index(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return View(fm);
}
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return Request.IsAjaxRequest() ? (ActionResult)PartialView("List", Service.Get(fm)) : View("Index", model);
}
With javascript enabled, this works fine - the content of div#result-list is updated as expected.
If I don't do the Request.AjaxRequest() and just return the PartialView, then with javascript disabled I get a page with just the content of the results on it. If I have the code as above, then I get a StackOverflowException.
How do I get this to work?
Solution
Thanks to #xixonia, I discovered the problem - here is my solution:
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue)
fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
if (Request.HttpMethod == "GET")
return PartialView("List", Service.Get(fm));
if (Request.HttpMethod == "POST")
return Request.IsAjaxRequest() ? (ActionResult) PartialView("List", Service.Get(fm)) : RedirectToAction("Index", model);
return new HttpStatusCodeResult((int) HttpStatusCode.MethodNotAllowed);
}
You can use the following extension method to determine if the request is an ajax request
Request.IsAjaxRequest()
If it is, you can return a partial view, otherwise you can return a full view or redirect.
if(Request.IsAjaxRequest())
{
return PartialView("view", model);
}
else
{
return View(model);
}
edit: here's the problem:
The "List" is returning the "Index" view when the request is not an AJAX request:
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return Request.IsAjaxRequest() ? (ActionResult)PartialView("List", Service.Get(fm)) : View("Index", model);
}
The "Index" view is rendering the "List" action:
#{ Html.RenderAction("List", Model); }
AKA: Recursion.
You need to engineer a way to display your list without drawing the index page, or make your index page draw a partial view with your list modal as a parameter.

Resources