Why does Ajax.BeginForm load index again before partialview? - c#

I am using Ajax.BeginForm to update a div with a partialview (loading logs based on the search input in the search fields).
The general idea is to load the Index the first time you log in with default values and then only update the log (partial view) when you search from there on.
The problem - When I debug my program it stops at Index in the controller before loading the partial view - resulting in long loading times.
The question - How can I make the Ajax request only load the partial view?
Code
_LogLayout.cshtml
<div id="log" class="tab">
<h1>Log</h1>
#using (Ajax.BeginForm("LogPartialView", "LogModelsController",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "divLogs",
}, new
{
id = "NewTableId"
}))
{
<p>#Html.TextBox("SearchString", null, new { #placeholder = "Message" })</p>
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
<p>
#Html.DropDownList("SelectedCustomer", Model.LogModelVmObject.CustomerList, new { #id = "logdropdownlabel", #class = "dropdownlabels" })
</p>
}
<p>
<input type="submit" class="standardbutton logsearch" name="submit" value="Search" />
</p>
}
#using (Html.BeginForm("ExportData", "LogModels"))
{
<input type="submit" name="export" class="standardbutton export" value="Export to Excel" />
}
<div id="divLogs">
#Html.Raw(ViewBag.Data)
#Html.Partial("_LogPartialLayout")
</div>
</div>
</div>
LogModelsController.cs
/// <returns>
/// Returns the populated log with the current customers information if the user is of the Role Member,
/// otherwise if the user is in the Role Admin - then show all customers logs by default.
/// </returns>
public async Task<ActionResult> Index()
{
if (Session["myID"] == null)
return ExpireSession();
const int pageNumber = 1;
var lmvm = new LogModelVm { CurrentSort = null };
var myId = Convert.ToInt32(Session["myID"].ToString());
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
_customer = _cdvdb.GetAllCustomerIds();
_message = _db.GetLogs();
}
else if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Member"))
{
_message = _db.GetLogsById(myId);
}
var logs = _message.OrderByDescending(i => i.Timestamp).ToPagedList(pageNumber, PageSize);
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
if (_customer != null)
{
var selectListItems = _customer as SelectListItem[] ?? _customer.ToArray();
foreach (var log in logs)
log.Name = selectListItems.FirstOrDefault(a => a.Value == log.CustomerId.ToString())?.Text;
lmvm.CustomerList = selectListItems;
}
}
lmvm.Logs = logs;
var model = new LogStatisticsModel
{
LogModelObject = new LogModel(),
StatisticsModel = await StatisticsData.GetAllCurrentStatisticsValues(1, DateTime.Now),
LogModelVmObject = lmvm
};
return View(model);
}
/// <returns>
/// Returns a partial view of the log.
/// </returns>
[HttpPost]
public ActionResult LogPartialView(string searchString, int? selectedCustomer, string currentMessageFilter, string currentCustomerFilter, int? page, string sortOrder)
{
// Some code.
return PartialView("_LogPartialLayout", model);
}
RouteConfig.cs
using System.Web.Mvc;
using System.Web.Routing;
namespace MyProject
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Login", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("Log", "{controller}/{action}/{Id}");
routes.MapRoute("Admin", "");
}
}
}
Index.cshtml
#model MyProject.Models.LogStatisticsModel
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "MyPortal";
Layout = "~/Views/Shared/_LogLayout.cshtml";
}

After a long discussion with #StephenMuecke which (among a ton of useful things) told me to try and comment out every single javascript in the _LogLayout.cshtml file and one by one uncomment them back and see if any of them caused the problem.
I found that the following script caused the problem.
<script type="text/javascript">
$('#loadercheck').click(function ()
{
$('#loader').show();
$.ajax(
{
success: function ()
{
$('#loader').delay(1200).hide(400);
}
});
})
</script>
The following script was used to display a spinning loader while the log was loading data. Apparently this caused the Index() to be called again. So I will have to find a new way to display a loader now...

Related

How to pass checkbox value to controller in ASP.NET MVC

I am using
#Html.CheckBoxFor(model => model.AllowOrder, new { id = "allowOrder"})
Now I want to pass its value (whether checked or unchecked) to the controller. I am using html.BeginForm for posting back the data to controller. Every time I am getting its value as null in action method. Action method has below sample code.
public ActionResult index(bool isChecked)
{
// code here
}
isChecked property is passed in as null always. Any help please. TIA.
If you don't want to return to controller whole data model, but only one value then see code below:
public IActionResult IndexTest()
{
var model = new ViewModel() { AllowOrder = true };
return View(model);
}
[HttpPost]
public IActionResult IndexTest(bool isChecked)
{
// your code here...
return View("IndexTest", new ViewModel() { AllowOrder = isChecked} );
}
Using the onclick() to trace the checkbox state:
#model ViewModel
<script>
function onStateChange() {
var item = document.getElementById('allowOrder');
var chk = false;
if (item.checked) {
chk = true;
}
document.getElementById('isChecked').value = chk;
};
</script>
#using (Html.BeginForm())
{
#Html.Hidden("isChecked", Model.AllowOrder)
#Html.CheckBoxFor(r => Model.AllowOrder, new { id = "allowOrder", #onclick = "onStateChange()" })
<input id="Button" type="submit" value="Save" />
}
View:
#model <specifyModelhere>
#using(Html.BeginForm("index","<YourControllerNameHere>",FormMethod.Post))
{
#Html.CheckBoxFor(r => Model.AllowOrder)
<input id="Button" type="submit" value="Save" />
}
Controller:
public ActionResult index(<YourModelNameHere> model)
{
var ischecked = model.AllowOrder;
// code here
}
This way when you submit the form, the entire model will be posted back and you can receive it in the controller method

C# Inject Javascript into View with JsonResult

I have a Controller Action which returns a JsonResult instead of a View. Depending on whether or not the method completed successfully, I need to add and execute a Javascript. I've added an OnActionExecuted ActionFilter to the method to generate and add the script, but because it's returning a JsonResult, I don't have a ViewResult to which I can add my script.
I'm a bit out of ideas on this. Does anyone have a solution to this problem or know of another way to approach this issue?
Controller method:
[InsertJavascript]
public async Task<ActionResult> Create(CreateAccountPageV2 currentPage, CreateAccountViewModel model)
{
//some logic here
return return Json(new
{
success = true,
redirectUrl = false,
html = partialViewToString,
invalidFields = InvalidFields
});
}
Action Filter:
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class InsertJavascriptAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonResult = filterContext.Result as JsonResult;
if (jsonResult == null)
{
return;
}
var script = GenerateMyJavascript();
// some way to send the script to View and execute it
}
}
Edit: The request is made from an Ajax Form contained in a Partial View.
Partial View file where the request is made to the Controller:
#model CreateAccountViewModel
#using (Ajax.BeginForm(null, null, new AjaxOptions
{
HttpMethod = "Post",
Url = Model.Url,
OnBegin = #OnBeginId,
OnSuccess = #OnSuccessId,
OnFailure = #OnFailureId
}, new
{
#class = "form--tight",
data_component = "Auth",
data_auth_type = "create",
data_id = #guid,
}))
{
<fieldset>
// input fields
<div class="Auth-createAccount-submitContainer">
<p class="required">*#Model.RequiredFieldLabel</p>
<button type="submit" id="createFormSubmitBtn" class="btn btn-primary Auth-createAccount-submitButton">
</button>
</div>
</fieldset>
}

My partial view is not being set up correctly

I have an application done in ASP.NET MVC and I am having an issue where my partial view is not being rendered after my form is submitted. What it does is that it reloads the entire page.
Here is my Model:
using System.Collections.Generic;
namespace Portfolio.Models
{
public class HomeViewModel
{
public List<User> User = new List<User>();
}
}
Here is my View:
#{
ViewBag.Title = "Home Page";
}
#model Portfolio.Models.HomeViewModel
#using (Ajax.BeginForm("FooForm",
new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "FooView"
}))
{
<button type:"submit" value:"Refresh/>
}
#Html.Partial("_FooView", Model)
<script>
window.onload = function () {
window.setTimeout(function () { $("#FooForm").submit(); }, 5000);
};
</script>
Here is my partial view:
#model Portfolio.Models.HomeViewModel
<div id="FooView">
#for (int i = 0; i < Model.User.Count; i++)
{
<form>
<div id="Name">#Model.User[i].Name</div>
<div id="Email">#Model.User[i].Email</div>
<div id="Date">#Model.User[i].Date</div>
</form>
<div>---------------------
</div>
}
</div>
Finally, here is my Controller:
using Portfolio.Models;
using System;
using System.Timers;
using System.Web.Mvc;
namespace Portfolio.Controllers
{
public class HomeController : Controller
{
public HomeViewModel model = new HomeViewModel();
Timer Timer = new Timer();
public ActionResult Index()
{
ModelState.Clear();
model.User.Add(new User() { Name = "A", Email = "a#email.com", Date = DateTime.Now.AddHours(-1) });
model.User.Add(new User() { Name = "B", Email = "b#email.com", Date = DateTime.Now.AddHours(-2) });
model.User.Add(new User() { Name = "C", Email = "c#email.com", Date = DateTime.Now.AddHours(-3) });
if (Request.IsAjaxRequest())
{
return PartialView("_FooView", model);
}
return View(model)
}
}
}
Can someone see why my Index Action is return the entire view and not the partial view upon Ajax request? Am I not setting my Ajax form properly? Many thanks in advance.
There are a few issues with your code.
First, your script does not submit your form because your form does not have an id attribute with a value of FooForm. Your Ajax.BeginForm() code does not add an id attribute (and is in fact calling a method in your controller named FooForm(). Assuming you really want to call the Index() method in your script then change the BeginForm code to
#using (Ajax.BeginForm("Index", null, new AjaxOptions(....), new { id = "FooForm" }))
which will generate <form .... id="FooForm" >
Then if your actually not hitting the code in the if (Request.IsAjaxRequest()) that returns the PartialView, it means your making a normal submit which means that you have not included jquery.unobtrusive-ajax.js in the view (or its not loading correctly or is loaded in the wrong order)
There is a missing double-quote on this line after Refresh: <button type:"submit" value:"Refresh/>

ASP MVC5 Partial view authentification redirection

I'm quite new with ASP so be tolerant :)
I've got a view with a search form in it.
View
<div id="search-form" class="row search-form">
#using(Ajax.BeginForm("Search",
"Home",
new AjaxOptions
{
UpdateTargetId = "result",
InsertionMode = InsertionMode.Replace,
LoadingElementId = "ajax_loader"
},
new { #class = "form-horizontal col-sm-offset-3 col-sm-6" }
))
{
<div class="form-group">
<div class="col-sm-10">
#{string query = string.Empty;}
#Html.EditorFor(x => query, new { htmlAttributes = new { #class = "form-control" } })
</div>
<input id="submitbtn" type="submit" value="Search" class="btn btn-primary col-sm-2" />
</div>
}
</div>
<div id="ajax_loader" style="display:none">
<img src="~/Content/Images/ajax_loader.gif" alt="Ajax Loader" />
</div>
<div id="result"></div>
Controller
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Title = "serach form";
return View();
}
public async Task<ActionResult> Search(string query)
{
WinesApiController winesCtrl = new WinesApiController();
var listOfWines = await winesCtrl.Get(query);
return PartialView("_WineResult", listOfWines);
}
The Search method in my controller returns a PartialView. When I decorate all the methodsin the controller with the [AllowAnonymous] attribute, everything works very well. But what I would like it to display the form for anybody, but as long as you click on the search button, you need to be logged in. So I deleted all the AllowAnonymous attributes but on the Index method (the one which render my View). Now, the result of my call is not shown anymore (which is quite ok) but I am not redirected to the login view.
My question is then, why the call to the partial view does not redirect me to the login view ? I guess that authentification is performed because I cannot see the results of the submit action, but why am I not redirected ?
You are making an ajax call to the Search Action right? The default asp.net mvc AuthorizeAttribute does not return an appropriate response when authorization fails on ajax calls.
You could write your own Authorization Filter, that returns a better response, like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class YourAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
var httpContext = filterContext.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
if (request.IsAjaxRequest()) {
response.SuppressFormsAuthenticationRedirect = true;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
}
base.HandleUnauthorizedRequest(filterContext);
}
}
With this filter, you could have a default javascript code to handle all unauthorized scenarios:
$.ajaxSetup({
statusCode: {
401: function (response) {
var returnUrl = encodeURI(window.location.pathname + window.location.search);
var loginUrl = '/custumer/login?ReturnUrl=' + returnUrl;
window.location.href = loginUrl;
}
}
});
What happens if you put an Authorize attribute on the action?
[Authorize()]
public async Task<ActionResult> Search(string query)
{
WinesApiController winesCtrl = new WinesApiController();
var listOfWines = await winesCtrl.Get(query);
return PartialView("_WineResult", listOfWines);
}

ASP.NET MVC Partial View issue when Submitting a Form

I am trying to display a Partial View inside a master (Index) View:
Steps:
User selects a Dropdown item from the Index View.
This displays a Partial View that has a search Form.
User fills the search Form and then clicks the Submit button.
If the search Form is valid, a new page (Results View) is displayed.
Else, the search Form Partial View should be re displayed with errors INSIDE the master View
I'm having a problem with number 4 because when the search Form submits, it only displays the partial View in a new window. I want to display the whole page : Index View + Partial View with errors.
Suggestions? This is what I have:
Image
Controller
public class AppController : Controller
{
public ActionResult Index()
{
return View(new AppModel());
}
public ActionResult Form(string type)
{
if (type == "IOS")
return PartialView("_IOSApp", new AppModel());
else
return PartialView("_AndroidApp", new AppModel());
}
public ActionResult Search(AppModel appModel)
{
if (ModelState.IsValid)
{
return View("Result");
}
else // This is where I need help
{
if (appModel.Platform == "IOS")
return PartialView("_IOSApp", appModel);
else
return PartialView("_AndroidApp", appModel);
}
}
}
Model
public class AppModel
{
public string Platform { get; set; }
[Required]
public string IOSAppName { get; set; }
[Required]
public string AndroidAppName { get; set; }
public List<SelectListItem> Options { get; set; }
public AppModel()
{
Initialize();
}
public void Initialize()
{
Options = new List<SelectListItem>();
Options.Add(new SelectListItem { Text = "IOS", Value = "I" });
Options.Add(new SelectListItem { Text = "Android", Value = "A"});
}
}
Index.cshtml
#{ ViewBag.Title = "App Selection"; }
<h2>App Selection</h2>
#Html.Label("Select Type:")
#Html.DropDownListFor(x => x.Platform, Model.Options)
<div id="AppForm"></div> // This is where the Partial View goes
_IOSApp.cshtml
#using (Html.BeginForm("Search", "App"))
{
#Html.Label("App Name:")
#Html.TextBoxFor(x => x.IOSAppName)
<input id="btnIOS" type="submit" value="Search IOS App" />
}
AppSearch.js
$(document).ready(function () {
$("#Platform").change(function () {
value = $("#Platform :selected").text();
$.ajax({
url: "/App/Form",
data: { "type": value },
success: function (data) {
$("#AppForm").html(data);
}
})
});
});
You need to call the search method by ajax too.
Change the index.html and then
1- if Form is valid replace the whole html or the mainContainer( The div that i have added to view).
2- else just replace the partial view.
#{ ViewBag.Title = "App Selection"; }
<div id="mainContainer">
<h2>App Selection</h2>
#Html.Label("Select Type:")
#Html.DropDownListFor(x => x.Platform, Model.Options)
<div id="AppForm"></div> // This is where the Partial View goes
</div>
Remove the form tag from your partial view just call an ajax call method for searching.
May be easiest way to handle this problem is using MVC unobtrusive ajax.
I would say use inline Ajax to submit this form.
#using (Html.BeginForm("Search", "App"))
{
#Html.Label("App Name:")
#Html.TextBoxFor(x => x.IOSAppName)
<input id="btnIOS" type="submit" value="Search IOS App" />
}
change upper given code into following code
#using (
Ajax.BeginForm(
"Form", "App",
new AjaxOptions()
{
UpdateTargetId = "App",
HttpMethod = "Post"
}
)
)
{
<div class="editor-label">
#Html.Label("App Name:")
</div>
<div class="editor-field">
#Html.TextBoxFor(x => x.IOSAppName)
</div>
<input id="btnIOS" type="submit" value="Search IOS App" />
}
//in controller change the parameter of the given method from string type to model object which will be posted by ajax form.
public ActionResult Form(AppModel appModel)
{
if (appModel.type == "IOS")
return PartialView("_IOSApp", new AppModel());
else
return PartialView("_AndroidApp", new AppModel());
}

Categories