Ajax form not submitting to controller action - ajax

Ajax form is not submitting to controller action. Here is the code
#using (Ajax.BeginForm("searchCustomers", "Transaction", new { phoneNumber = Model.CustomerMobile }, new AjaxOptions
{
UpdateTargetId = "custList",
InsertionMode = InsertionMode.Replace
}))
{
<div class="col-md-6">
<div class="form-group">
<label>Customer Mobile No:</label>
#Html.TextBoxFor(x => x.CustomerMobile, new { #class = "form-control", id = "custMobile" })
</div>
#*<div class="form-group">
<label>Customer Name</label>
#Html.TextBoxFor(x => x.CustomerName, new { #class = "form-control", id = "custName" })
</div>*#
<input type="submit" class="btn btn-default" value="Get Customer Details" >
</div>
}
Here is the controller action
public ActionResult searchCustomers(string phoneNumber)
{
if (phoneNumber==null)
{
return PartialView(new List<Models.Customer>());
}
var c = Database.Session.Query<Models.Customer>()
.Where(x => x.MobileNumber.Like(phoneNumber) )
.ToList();
return PartialView(c);
}
but the ajax form is not submitting. I've added the JavaScript files as bundles. I've another #Html.Action("searchCustomers", new { phoneNumber = Model.CustomerMobile }) this one calls the controller action.

Everything is fine in your code. There are two javascript files that are needed for Ajax.Beginform to work.
jquery-{Vaersion}.js
jquery.unobtrusive-ajax.js
Check whether you have included those files to your view or not. Or if your view has any LayOut if those javascript files are included in your LayOut or not.

Related

ajax post double triggers each time sending form

i working with asp.net mvc5 and trying to post a form to the controller without refreshing the page using ajax. but the problem is that each time i press the submit button, it sends the data many times. only the first time it submits the form once. but the second time it send twice, and the third time it sends four times and ...
here is my controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using UniProject.Models;
namespace UniProject.Controllers
{
public class TestController : Controller
{
MyContext db = new MyContext();
// GET: Test
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Create(Test test)
{
try
{
if (ModelState.IsValid)
{
db.Tests.Add(test);
db.SaveChanges();
ViewBag.Record = "Data Inserted";
return PartialView("Index");
}
else
{
ViewBag.Record = "Error";
return View(test);
}
}
catch
{
ViewBag.Record = "Error";
return View();
}
}
}
}
this is my view
#model UniProject.Models.Test
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<h2>Index</h2>
<div id="div">
#Html.Partial("Form" , Model)
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My PartialView
#model UniProject.Models.Test
#using (Ajax.BeginForm("Create" , "Test" , new AjaxOptions { HttpMethod = "post" , UpdateTargetId = "div"}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Test</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.TestName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TestName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TestName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TestTitle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TestTitle, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TestTitle, "", 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>
}
this the form view after once clicking submit button
Form Image
and this is the database image
Database Records
please help me with this issue. thanks in advance...

MVC 5 Ajax form with field updates asynchronously and still supporting a Full form post

So I have been using MVC for a while but not a lot of Ajax.
So the issue i have is that I have a create view where the first field of the create view is a Date picker which on change i want ajax to change the selection dropdown of another field on the form. I have the Ajax update working , but the form Post (Create button), now only calls into the Ajax method on the controller. I want the Ajax post back to call the default method Create method on the Controller. So the Create form contains 3 fields
A date (which has the OnChange Ajax submit)
A drop down list of id's and text
other fields as required
I have included the model and cshtml view files (one the partial). The controller is just simply a method taking either the datetime value or the entire model.
So I have the code working where, when the date changes it updates the relevant LocalIds field, but because the 'create' button is inside the Ajax.BeginForm tags, when the create button is pressed, it generates an Ajax call, and I want it to generate a Form Post. What am i missing
CreateModel
public class IdsSelection
{
public string Id { get; set; }
public List<SelectListItem> Selections { get; set; }
}
public class CreateModel
{
[Display(Name="Local Id's")]
public IdsSelection LocalIds;
[Display(Name="Day")]
[Required, DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}",ApplyFormatInEditMode=true)]
public DateTime Date { get; set; }
[Required, Display(Name = "Other Value")]
[Range(1.0, 1000000.0, ErrorMessage ="Value must be greater than zero")]
public decimal OtherValue { get; set; }
public CreateModel()
{
Date = DateTime.Today;
}
}
CreateView.cshtml
#Model Demo.Models.CreateModel
....
#using (Ajax.BeginForm("ChangeDate", "Process", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
UpdateTargetId = "changeid",
}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new
{
htmlAttributes = new
{
autofocus = "autofocus",
#class = "form-control",
#Value = Model.Date.ToString("yyyy-MM-dd"),
onchange = "$(this.form).submit();"
}
})
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
#Html.Partial("_ShowIds", Model.LocalIds)
<div class="form-group">
#Html.LabelFor(model => model.OtherValue, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OtherValue, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OtherValue, "", 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>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
}
_ShowIds.cshtml
#model Demo.Models.IdsSelection
<div id="changeid" class="form-group">
#Html.Label("Local Id", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#if(Model.Selections.Count == 1) // one
{
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(model => model.Selections[0].Text, new
{
htmlAttributes = new
{
#class = "form-control",
#readonly = "readonly",
}
})
}
else // more entries so show a dropdown
{
#Html.DropDownListFor(model => model.Id, Model.Selections, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model, "", new { #class = "text-danger" })
}
</div>
</div>
You have not shown you controller methods, but assuming the method which generates this view is named Create(), then create a corresponding POST method
[httpPost]
public ActionResult Create(CreateModel model)
for saving the data. The remove #using (Ajax.BeginForm("ChangeDate", "Process", new AjaxOptions() { ..... ])) and replace with a normal form to post back to the server (and you can remove jquery.unobtrusive-ajax.min.js)
#using (Html.BeginForm())
{
....
Next create a controller method that returns your partial view based on the selected date
public PartialViewResult FetchLocalIds(DateTime date)
{
IdsSelection model = // populate model based on selected date
return PartialView("_ShowIds", model);
}
Next, in the view, wrap the current #Html.Partial() in a placeholder element
<div id="localids">
#Html.Partial("_ShowIds", Model.LocalIds)
</div>
Then use jquery to handle the datepickers .change() event and call the FetchLocalIds() method to update the DOM using the .load() function.
$('#Date').change(function() {
$('#localids').load('#Url.Action("FetchLocalIds")', { date: $(this).val() })
})
Note also to remove onchange = "$(this.form).submit();" from the #Html.EditorFor(model => model.Date, new { ... })

Ajax.BeginForm only displaying partial View instead of parent & partial view on return

I have a parent View & a child View.
When posing with the Ajax.BeginForm, I'm expecting back the entire parent view plus the results of the partial view updated. Only the results of the partial view is displayed.
In addition, the "OnSuccess" method doesn't seem to be getting hit as I'm debugging.
Can someone please tell me what I'm doing incorrecty?
Controller:
public class HomeController : Controller
{
private DAL db = new DAL();
public ActionResult Index()
{
ViewBag.Message = "Welcome to YeagerTech!";
return View();
}
// GET: Categories/Create
public ActionResult Create()
{
return View();
}
// POST: Categories/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(Category category)
{
if (ModelState.IsValid)
{
try
{
await db.AddCategoryAsync(category);
}
catch (System.Exception ex)
{
throw ex;
}
}
return View(category);
}
public PartialViewResult ShowDetails()
{
//string code = Request.Form["txtCode"];
Category cat = new Category();
//foreach (Product p in prodList)
//{
// if (p.ProdCode == code)
// {
// prod = p;
// break;
// }
//}
cat.CategoryID = 1;
cat.Description = "Financial";
return PartialView("_ShowDetails", cat);
}
}
Parent View
#model Models.Models.Category
#{
ViewBag.Title = "Create Category";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Category</h2>
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
#*<div>
#Html.ActionLink("Back to List", "Index")
#Html.Hidden("categoryCreateUrl", Url.Action("Create", "Home"))
</div>*#
#section Scripts {
<script>
$(document).ready(function ()
{
function OnSuccess(response)
{
$('#form1').trigger("reset");
}
//if (typeof contentCreateCategory == "function")
// contentCreateCategory();
});
</script>
}
Partial View
#model Models.Models.Category
<h1>Cat Details</h1>
<h2>
Cat Code: #Model.CategoryID<br />
Cat Name: #Model.Description<br />
</h2>
EDIT # 1
Parent & child view displayed (due to a missing JS file, thanks to the mention of Stephen), plus was able to programmatically clear out the form with the OnSuccess method.
I had thought since that was JS, it needed to go in the Scripts section, but did not recognize it there.
Here is the finished code.
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #id = "frm1", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
<button type="reset" id="btnClear" class="btn btn-default"><span class="glyphicon glyphicon-eye-close"></span>Clear</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
<script type="text/javascript">
function OnSuccess(response)
{
$('#frm1').trigger("reset");
}
function OnFailure(response)
{
alert("Whoops! That didn't go so well did it?");
}
</script>
A bit late for answer, but yet it might help someone else.
The issue might be that the javascript files required for Ajax.BeginForm are not loaded in your page.
Microsoft.jQuery.Unobtrusive.Ajax
Nuget package to search for
<package id="Microsoft.jQuery.Unobtrusive.Ajax" version="3.2.3" targetFramework="net45" />
of course that the version might differ.
Add reference to the page after your jQuery and it should work.

Why does Unobtrusive Ajax require a layout page (returning a partial view doesn't work)?

While creating a custom plugin, that Ajaxifies any forms that are not already Ajaxified by unobtrusive Ajax, I noticed that my partial/layout code failed to display on post-back with a normal Unobtrusive Ajax form:
For my other partial-update plugins, I conditionally render pages as partial (for ajax requests) or full with layout (for normal requests) like this:
public ActionResult AjaxForm()
{
if (!Request.IsAjaxRequest())
{
ViewBag.Layout = "_Layout.cshtml";
}
return PartialView();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AjaxForm(AjaxFormModel model)
{
if (!Request.IsAjaxRequest())
{
ViewBag.Layout = "_Layout.cshtml";
}
if (ModelState.IsValid)
{
return PartialView("Success", model);
}
return PartialView(model);
}
When it came to testing a normal unobtrusive Ajax form, I used this test view, which updates just its own element UpdateTargetId="FormPanel":
#using (Ajax.BeginForm("AjaxForm", null, new AjaxOptions() { UpdateTargetId = "FormPanel" }, new { id = "FormPanel" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AddressLine, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AddressLine, new { htmlAttributes = new { #class = "form-control" } })
</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>
}
If I remove the test if (!Request.IsAjaxRequest()), and return the full page with master page/layout, it renders the result just fine where the form was.
Fiddler shows the "success" view is returned in either case, but it does not do anything without the layout present.
Q. What is it in a masterpage/layout that unobtrusive Ajax requires in order to extract content and replace the specified element
Update:
To make things worse it actually works correctly the first time, but not if I ajax load the same form again.
A: Forms must have unique IDs!
There are loads of issues about unique IDs on SO, which browsers generally cope with, but the one you cannot ever break is having two forms loaded with the same id. Only one is visible to JavaScript/jQuery!
My problem was caused by a duplicate form ID. You should not insert the result page back in to the form itself (leaving the form element intact).
When I reloaded the original form, dynamically in a slideshow-style control, I wound up with duplicate form ID's.
This meant Unobtrusive Ajax could only see the first form in the DOM!
Solution (do not set Ajax forms to update themselves):
Set UpdateTargetId to an element above the form, never the form itself if you load panels dynamically.
Fix to my example view (surround form with a target panel):
<div id="FormPanel">
#using (Ajax.BeginForm("AjaxForm", new AjaxOptions() { UpdateTargetId = "FormPanel" }))

when using enctype="multipart/form-data" form doesn't post

I'm working on an app on ASP.Net MVC 4 and am trying to upload an image in a form. The problem I'm having is that when the form posts, if the <input type="file"... is empty (meaning I have not selected a file) the form posts just fine and everything works. However, When I do select a file the form just sits there and does nothing.
At first I thought it was just taking it a while to upload but I have left it for quite some time (file size was 7kb) and nothing. the debugger doesn't even hit the breakpoints at the beginning of the action either. I'm at a loss since I'm quite new to the platform and am still learning every day. Below please find all the relevant code:
the View:
#using (Html.BeginForm("StaffInformation", "Manager", FormMethod.Post, new { #class = "form-horizontal", enctype = "multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="control-group">
#Html.LabelFor(model => model.firstName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(model => model.firstName, new { #class = "span12" })
#Html.ValidationMessageFor(model => model.firstName)
</div>
</div>
<div class="control-group">
#Html.LabelFor(model => model.lastName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(model => model.lastName, new { #class = "span12" })
#Html.ValidationMessageFor(model => model.lastName)
</div>
</div>
<div class="control-group">
Staff Photo:
<div class="controls">
#if (Model.staffImage == null)
{
#:None
}else{
<img width="150" height="150" src="#Url.Action("GetImage", "Manager", new { Model.staffProfileID })" /><br />
}
<input type="file" id="staffImage" name="staffImage" data-provide="fileinput">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<a href="#Url.Action("dashboard", "manager")" class="btn" type="reset" >Cancel</a>
</div>
}
The Action:
public ActionResult StaffInformation(StaffInformationViewModel model, HttpPostedFileBase staffImage)
{
if (ModelState.IsValid) //This is where the breakpoint is
{
if (staffImage != null) {
model.imageMimeType = staffImage.ContentType;
model.staffImage = new byte[staffImage.ContentLength];
staffImage.InputStream.Read(model.staffImage, 0, staffImage.ContentLength);
}
using (var repo = new CompanyInformationRepository(new UniteOfWorkCompanies()))
{
var company = repo.FindCompany(User.Identity.Name);
repo.AddOrUpdateStaff(model, company);
}
return RedirectToAction("ManageStaff");
}
}
I really don't know whats going on and simply the reason I'm using the enctype = "multipart/form-data" is because I was reading the "Pro ASP.NET MVC 4" by Adam Freeman and in there they said that it won't work without it. Like I said, I'm quite new and need all the help I can get.
You can try to put the HttpPostedFileBase staffImage as part of your ViewModel (StaffInformationViewModel) - also yes, you need to keep the enctype = "multipart/form-data" in the form tag:
public class StaffInformationViewModel
{
// your other properties here ...
[Required]
public HttpPostedFileBase StaffImage { get; set; }
}
Then in the View:
#Html.TextBoxFor(model => model.StaffImage, new { type = "file" })
Then in the Controller:
public ActionResult StaffInformation(StaffInformationViewModel model)
{
if (ModelState.IsValid)
{
if (model.StaffImage != null)
{
// awesome
}
// ... more awesome
}
}
I hope this helps. Let me know if you need more clarifications here on this one ...

Resources