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.
Related
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");
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.
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>
Can anyone tell whats wrong with my view? my model values is displayed correctly in my view but it is not returned to the controller action. public ActionResult DeleteConfirmed(ClientModel contacts) my ClientModel is returned null.
View:
#model ContactManager.Models.ClientModel
#{
ViewBag.Title = "Delete Information";
}
<h2>Delete Information</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>Details</legend>
<div class="display-label">ID</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.Client00130012)
</div>
<div class="display-label">Name</div>
<div class="display-field">
#Html.DisplayFor(model => model.Clientes0013.Nombre0013)
</div>
<div class="display-label">Contact Number</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.Direcc0012)
</div>
<div class="display-label">Country</div>
<div class="display-field">
#Html.DisplayFor(model => model.CanaClie0012.F1Pais00200012)
</div>
</fieldset>
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
Controller Action:
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(ClientModel contacts)
{
contacts.Clientes0013.Client0013 = contacts.CanaClie0012.Client00130012;
contacts.Clientes0013.F1Pais00200013 = contacts.CanaClie0012.F1Pais00200012;
Clientes0013 clientes0013 = db.Clientes0013.Find(contacts.Clientes0013.Client0013, contacts.Clientes0013.F1Pais00200013);
db.Clientes0013.Remove(clientes0013);
db.SaveChanges();
return RedirectToAction("Index");
}
1. Keep the helpers inside the Form like below.
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
#Html.DisplayFor(i => i.PropertyName);
<input type="submit" name="Submit" value="Submit" />
}
2. In order to Submit the form and send the model in Post Action Method, you have to use a Hidden Field along with each DisplayFor Helper.
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
#Html.HiddenFor(i => i.PropertyName);
#Html.DisplayFor(i => i.PropertyName);
<input type="submit" name="Submit" value="Submit" />
}
3. You will not be able to see the Model values in Post Action Method when the View is like below..
#Html.HiddenFor(i => i.PropertyName);
#Html.DisplayFor(i => i.PropertyName);
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, null))
{
<input type="submit" name="Submit" value="Submit" />
}
This is because you are using DisplayFor for all your properties. DisplayFor will not send anything in a POST, it doesn't create an input.
You could add #Html.HiddenFor(model => model.CanaClie0012.Client00130012) , etc. to each one of your properties in order for your view to send anything back.
I create view by model Article.
class Article
{
public string Title {get;set;}
public List<string> Terms {get;set}
}
Terms - can be any count, and I want that they can be added gradually
#using (Html.BeginForm("Create"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>CreateArticle</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
Terms:
</div>
<div id="divList">
</div>
#using (Ajax.BeginForm("Update", new AjaxOptions() { Confirm = "Add", HttpMethod = "Post", UpdateTargetId = "divList", InsertionMode = InsertionMode.InsertAfter }))
{
#Html.Partial("_TermPP", "")
<input id="count" name="count" type="hidden" value="-1" />
<input type="submit" value="add" onclick="javascript:plus()" />
}
</fieldset>
}
_TermPP:
#model String
<div>
<input type="text" name="terms[#(ViewBag.Count==null?0:ViewBag.Count)]" value="#(Model == null ? "" : Model)" /> </div>
when the click is sent to a form of ADD but I need to create on the Update. How do this?
You may take a look at the following blog post. Also please note that you cannot nest 2 <form> elements as you did in your code - this is invalid HTML and might result in undefined behavior.