MVC 5 Ajax posting whole form - instead of ajax call - ajax

I have a basic ajax call setup in MVC 5 but it seems that my Ajax form is actually posting the full form, instead of getting back PartialViewResult in the main view, the whole window just renders with the PartialView for the result
suggestion what I may be missing here ?
I also do have the following jquery renders in my _Layout.cshtml
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
MainView
#{
ViewBag.Title = "Puzzles 1 & 2";
}
<h2>#ViewBag.Title</h2>
<div>
#Html.Partial("Puzzle1Form")
</div>
PartialView
#model SixPivot_Code_Puzzles.Models.Puzzle1Model
#using (Ajax.BeginForm("Puzzle1","Puzzles",new AjaxOptions {
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "puzzle1-result",
}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Puzzle1</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.IntegerList, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.IntegerList, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.IntegerList, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Find Largest No." class="btn btn-default" />
</div>
</div>
</div>
}
<div id="puzzle1-result"></div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
}
Controller
[HttpPost]
public PartialViewResult Puzzle1(Puzzle1Model model)
{
if (ModelState.IsValid)
{
Puzzle1Model result = new Puzzle1Model();
result.LargestInteger = FindLargestInt(model.IntegerList).ToString();
result.IntegerList = model.IntegerList;
return PartialView("Puzzle1FormResult",result);
}
else {
return PartialView("Puzzle1Form",model);
}
}
PartialViewResult on Success (Puzzle1FormResult.cshtml)
#model SixPivot_Code_Puzzles.Models.Puzzle1Model
<div>
<h4>Largest Integer</h4>
<hr />
<p>
Largest Integer for the list "#Model.IntegerList" is : #Model.LargestInteger
</p>
</div>

I tend to try not use the Ajax helpers in MVC because I find jQuery easier to understand. You could try doing it how I would.
PartialView
#model SixPivot_Code_Puzzles.Models.Puzzle1Model
<form class="form-horizontal" id="frmPuzzle1">
#Html.AntiForgeryToken()
<div>
<h4>Puzzle1</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.IntegerList, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.IntegerList, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.IntegerList, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Find Largest No." class="btn btn-default" />
</div>
</div>
</div>
<div id="puzzle1-result"></div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
*Notice I removed the scripts section, you should have this in your layout instead.
MainView
#{
ViewBag.Title = "Puzzles 1 & 2";
}
<h2>#ViewBag.Title</h2>
<div>
#Html.Partial("Puzzle1Form")
</div>
<script>
$(document).ready(function() {
$(document).on('submit', '#frmPuzzle1', function(e) {
// stop default form submission
e.preventDefault();
$.ajax({
url: '#Url.Action("Puzzle1", "Puzzles")',
type: 'POST',
data: $('#frmPuzzle1').serialize(),
success: function(html) {
$('#puzzle1-result').html(html);
}
});
});
});
</script>

The Ajax.* family of helpers simply add a standard HTML setup (a regular old form, for example) and some JavaScript that intercepts the default behavior, sending it as AJAX instead. In other words, the code is unobtrusive. If for whatever reason the JavaScript can't be run, it will fallback to standard behavior of doing a simple form post.
Therefore, if it's doing a standard form post, rather than sending an AJAX request, you most likely have some JavaScript error on the page that is preventing the AJAX code from running.

The Ajax.* family of helpers simply add a standard HTML setup (a regular old form, for example) and some JavaScript that intercepts the default behavior, sending it as AJAX instead. In other words, the code is unobtrusive. If for whatever reason the JavaScript can't be run, it will fallback to standard behavior of doing a simple form post.

Related

ASP.NET MVC - data validation for modal window doesn't work

In my MVC application I have simple model for project:
public class Project
{
public int Id { get; set; }
[Required]
[MinLength(5, ErrorMessage = "Project name must have at leat 5 characters")]
public string ProjectName { get; set; }
[Required]
[MinLength(10, ErrorMessage = "Project description name must have at leat 10 characters")]
public string ProjectDescription { get; set; }
public ICollection<BugTrackerUser> Users { get; set; }
public Project()
{
Users = new List<BugTrackerUser>();
}
}
On the index page I have button that triggers bootstrap modal window. As a button, modal itself located in Index.chtml, but form that belongs to this model located in partial view _addProjectPartialView:
<form method="post" id="projectForm">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectName">Project Name</label>
<input asp-for="ProjectName" class="form-control" />
<span asp-validation-for="ProjectName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectDescription">Project Description</label>
<input asp-for="ProjectDescription" class="form-control" />
<span asp-validation-for="ProjectDescription" class="text-danger"></span>
</div>
In HomeController I have this post method for adding new project:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddProject(Project project)
{
if (ModelState.IsValid)
{
_repository.AddProject(project);
return RedirectToAction("Index", "Home");
}
return PartialView("_addProjectPartialView", project);
}
Logic of adding new project works fine, and I have all references that i need, jquery, jquery-validation and jquery-validation-unobtrusive, but I have problems with validation. Validation errors doesn't displays in my modal window. What can I try to solve this problem? I already read a bunch of tutorials, related questions etc, but it seems to me that there is no problems with code. I only have doubts with my return statement in AddProject method, should I return something else?
I solved this problem by removing partial view. Form that I Have in partial view and modal window I placed in Index.cshtml, also instead of asp.net tag helpers I use Html.BeginForm, and it works fine. Here is my modal:
<div class="modal fade" id="addProjectModal" tabindex="-1" aria-labelledby="addProjectModallLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
#using (Html.BeginForm("AddProject", "Home", FormMethod.Post))
{
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Project Information</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(p => p.Id)
<div class="form-group">
#Html.LabelFor(p => p.ProjectName, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectName, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(p => p.ProjectDescription, htmlAttributes: new { #class = "control-label col-3" })
<div class="col-9">
#Html.TextBoxFor(p => p.ProjectDescription, new { #class = "form-control" })
#Html.ValidationMessageFor(p => p.ProjectDescription, "", new { #class = "text-danger" })
</div>
</div>
#*<partial name="_addProjectPartialView" />*#
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="btnSave">Save</button>
</div>
</div>
}
</div>
</div>
I also using ajax post method to add projects and render list of projects async on the client side:
<script type="text/javascript">$(document).ready(function () {
$('#btnSave').click(function () {
var projectData = $('#projectForm').serialize();
$.ajax({
type: "POST",
url: "/Home/AddProject",
data: projectData,
success: function () {
window.location.href = "/Home/Index";
}
})
})
})
$('#closeButton').click(function () {
$('#addProjectModal').modal('hide');
})</script>
But I still want to understand how to solve this problem, because using partial views have some benefits
I think the problem here is , when you triggers the modal that modal doesn't have the references to (validator unobtrusive).
So try manually register this where you triggers the modal like this
$.validator.unobtrusive.parse("#idForModal");

Use ajax in asp.net mvc 5 in a tab

I want to use ajax to update the content of a TAB and it doesn't work, when I press the buton submit i update all the page. I don't know how to solve it.
Thanks.
Controller PeticioUsuarisController:
// POST: PeticioUsuaris/_Demanar
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _Demanar([Bind(Include = "Nom,PrimerCognom,SegonCognom")] PeticioUsuari peticioUsuari)
{
if (ModelState.IsValid)
{
peticioUsuari.IdUsuariFaPeticio = 1;
db.PeticioUsuari.Add(peticioUsuari);
db.SaveChanges();
return PartialView("_PeticioCorrecte");
}
return PartialView("_PeticioCorrecte");
}
View Index.cshtml:
#{
ViewBag.Title = "";
}
<!-- Tab Buttons -->
<ul id="tabstrip" class="nav nav-tabs" role="tablist">
<li class="active">Demanar</li>
<li>Acceptar</li>
</ul>
<!-- Tab Content Containers -->
<div class="tab-content">
<div class="tab-pane fade in active" id="_Demanar">#Html.Partial("_Demanar")</div>
<div class="tab-pane fade" id="_AcceptarPeticio"></div>
</div>
#section scripts {
<script>
$('#tabstrip a').click(function (e) {
e.preventDefault()
var tabID = $(this).attr("href").substr(1);
$(".tab-pane").each(function () {
console.log("clearing " + $(this).attr("id") + " tab");
$(this).empty();
});
//$("#" + tabID).load("/#ViewContext.RouteData.Values["controller"]/" + tabID)
$.ajax({
url: "/#ViewContext.RouteData.Values["controller"]/" + tabID,
cache: false,
type: "get",
dataType: "html",
success: function (result) {
$("#" + tabID).html(result);
}
})
$(this).tab('show')
});
</script>
}
View _Demanar:
#model Peticions.Models.PeticioUsuari
#{
AjaxOptions options = new AjaxOptions
{
HttpMethod = "Post",
UpdateTargetId = "content",
InsertionMode = InsertionMode.InsertAfter
};
}
#using (Ajax.BeginForm("_Demanar", "PeticioUsuaris", null, options))
{
#Html.AntiForgeryToken()
<div class="form-horizontal tabs">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Nom, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Nom, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Nom, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PrimerCognom, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PrimerCognom, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PrimerCognom, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SegonCognom, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SegonCognom, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SegonCognom, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default"/>
</div>
</div>
</div>
}
<div id="content"></div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
View _PeticioCorrecte:
#model Peticions.Models.PeticioUsuari
<div class="alert alert-success">
<a href="#Url.Action("Index", "PeticioUsuaris", new { id = UrlParameter.Optional })">
PeticiĆ³ enviada correctament! Clica aquĆ­ per a crear-ne un altre.
</a>
</div>
try this
#{
AjaxOptions options = new AjaxOptions
{
HttpMethod = "Post",
UpdateTargetId = "formContent"
};
}
#using (Ajax.BeginForm("_AcceptarPeticio", "PeticioUsuaris", null,options ))
{
}

MVC 5 HTML helper for client side validation for value not in model

I'm following the MVC tutorial here to add a search/filter textbox:
http://www.asp.net/mvc/overview/getting-started/introduction/adding-search
I've added the filter textbox to my page:
#using (Html.BeginForm()) {
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
How do I perform client side validation on the textbox to ensure something was entered?
When you automatically created the controller and views based on a model, it creates this handy HTML helper:
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
Can something similar be used for a value not in your model or do you have to add custom jquery/html?
Edit:
Thanks for the direction everyone. Just adding some more information now. I've created a new class in my existing model:
public class SearchItem
{
[Required]
[StringLength(100, MinimumLength = 5, ErrorMessage = "This is my test error message")]
public string SearchString { get; set; }
}
Then inside the controller, I've created a partial view:
[ChildActionOnly]
[AllowAnonymous]
public PartialViewResult Search()
{
return PartialView();
}
Then I've created a new Search.cshtml with:
#model App.Models.SearchItem
#using (Html.BeginForm())
{
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<h4>#Html.LabelFor(model => model.SearchString, "Search:", htmlAttributes: new { #class = "control-label col-md-2" })</h4>
<div class="col-md-10 input-max-width">
#Html.EditorFor(model => model.SearchString, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SearchString, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<input type="submit" value="Search" class="btn btn-primary" />
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Then on my existing page, I add it with this:
#Html.Action("Search", "Items")
When I enter no text into the textbox and click the Search button, the page just refreshes and I get no client side validation.
My HTML looks like this:
<form action="/Items/Index" method="post"> <div class="form-group">
<h4><label class="control-label col-md-2" for="SearchString">Search:</label></h4>
<div class="col-md-10 input-max-width">
<input class="form-control text-box single-line" data-val="true" data-val-length="This is my test error message" data-val-length-max="100" data-val-length-min="5" data-val-required="The SearchString field is required." id="SearchString" name="SearchString" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="SearchString" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Search" class="btn btn-primary" />
</div>

FormCollection parameter is empty while using #Ajax.BeginForm and #Ajax.ActionLink

I've went through whole bunch of SO questions regarding my problem and I wasn't able to find a solution, so I've decided to post this question. The problem is that FormColection fc is empty when I do that Ajax POST request by clicking the search button from _Sidebar.cshtml. What am I missing here?
Code part:
_MyLayout.cshtml:
#using (Ajax.BeginForm("Results", "Search", null, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "d_content", LoadingElementId = "spinner", OnBegin = "ToggleContent", OnComplete = "ToggleContent" },null))
{
<div class="wrapper row-offcanvas row-offcanvas-left">
<div id="a_sidebar">
#{ Html.RenderAction("_Sidebar"); }
</div>
<aside class="right-side">
<section class="content">
<img id="spinner" src="~/Content/img/lightbox-ico-loading.gif" style="display: none;">
<div id="d_content">
#RenderBody()
</div>
</section>
</aside>
</div>
}
_Sidebar.cshtml:
<ul class="treeview-menu">
<li>
<div class="form-group">
<label>F1</label>
#Html.ListBox("ddF1", (MultiSelectList)ViewData["ddF1"])
</div>
<div class="form-group">
<label>F2</label>
#Html.ListBox("ddF2", (MultiSelectList)ViewData["ddF2"])
</div>
<div class="form-group">
<label>Status</label>
#Html.ListBox("ddStatusFilter", (MultiSelectList)ViewData["ddStatusFilter"])
</div>
<div class="form-group">
<label>Name</label>
#Html.TextBox("tbName")
</div>
</li>
</ul>
<li class="treeview">
<div style="text-align: center;">
<button type="reset" class="btn btn-warning">Clear Filters</button>
#Ajax.ActionLink("Search", "Results", "Search", new { }, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "d_content", LoadingElementId = "spinner", OnBegin = "ToggleContent", OnComplete = "ToggleContent" }, new { #class = "btn btn-success", #style = "color: white;" })
</div>
</li>
SearchController.cs:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult Results(FormCollection fc)
{
//TODO: Implement filtering through FormCollection in passed parameter
// but that freakin' fc var is empty
var model = db.vw_MainTable.ToList();
return PartialView("_SearchResultGrid", model);
}
Much appreciated.
From the code, It looks like you don't need a form tag covering your entire html. You can place it in the _Sidebar.cshtml like this.
Also replace the action link with a submit button (check below code).
#using (Ajax.BeginForm("Results", "Search", null, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "d_content", LoadingElementId = "spinner", OnBegin = "ToggleContent", OnComplete = "ToggleContent" },null))
{
<ul class="treeview-menu">
<li>
<div class="form-group">
<label>F1</label>
#Html.ListBox("ddF1", (MultiSelectList)ViewData["ddF1"])
</div>
<div class="form-group">
<label>F2</label>
#Html.ListBox("ddF2", (MultiSelectList)ViewData["ddF2"])
</div>
<div class="form-group">
<label>Status</label>
#Html.ListBox("ddStatusFilter", (MultiSelectList)ViewData["ddStatusFilter"])
</div>
<div class="form-group">
<label>Name</label>
#Html.TextBox("tbName")
</div>
</li>
</ul>
<li class="treeview">
<div style="text-align: center;">
<button type="reset" class="btn btn-warning">Clear Filters</button>
<button type="submit" class="btn"> Search</button>
</div>
</li
}
There might be multiple reasons for this. In my case, it was happening because my form fields only had the ID property and not the Name property.
After adding the Name property to the input fields, the Controller could see them just fine.

Ajax GET request calling POST instead (ASP.NET MVC5)

I've created an ajax GET call to perform a search feature. But every time the search button is clicked, it is calling the POST instead (thereby returning null error for the model). I am not sure what I have done wrong. Care to give a hand please?
My controller:
//
// GET: /DataEntry/ChargeBack
public ActionResult ChargeBack(string dwName, string searchTerm = null)
{
var model = createModel();
if (Request.IsAjaxRequest())
{
return PartialView("_Suppliers", model);
}
return View(model);
}
//
// POST: /DataEntry/ChargeBack
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ChargeBack(ChargeBackViewModel model)
{
if (ModelState.IsValid)
{
model.someAction();
}
return View(model);
}
My Main View:
#model CorporateM10.Models.ChargeBackViewModel
#using (Ajax.BeginForm(
new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "SuppliersList"
}))
{
#Html.AntiForgeryToken()
<input type="search" name="searchTerm" class="form-control col-md-2" />
<input type="submit" value="Search by Name" class="btn btn-info" />
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#Html.Partial("_Suppliers", Model)
<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>
}
My Partial View:
#model CorporateM10.Models.ChargeBackViewModel
<div class="form-group" id="SuppliersList">
<div class="col-md-10">
#Html.EditorFor(model => model.Suppliers)
#Html.ValidationMessageFor(model => model.Suppliers)
</div>
</div>
I've found it. It is being caused by not including jquery.unobtrusive-ajax library. Once included, the Ajax action works as intended.

Resources