Ajax.Begin Form client side validation in Partial View MVC5.2 - javascript

Index.cshtml:
<div class="search-ctn">
#{ Html.RenderAction(MVC.Home.Search()); }
</div>
Search.cshtml:
#model ViewModels.SearchVM
#using (Ajax.BeginForm(MVC.Home.Search(), new AjaxOptions { UpdateTargetId = "idx-list-ctn", InsertionMode = InsertionMode.Replace }, new { #id = "sch-frm" }))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(m => m.Service)
#Html.ValidationMessageFor(m => m.Service)
}
MVC.Home.Search Action:
[HttpPost, ValidateAntiForgeryToken]
public virtual PartialViewResult Search(SearchVM partnerSearch)
{
// Search made here
}
ViewModel:
public class SearchVM
{
[Required]
public string Service { get; set; }
}
Libraries:
jquery-2.1.3.js
jquery.unobtrusive-ajax.js (v3.2.3)
jquery.validate.js (v1.13.1)
jquery.validate.unobtrusive.js (v3.2.3)
In an old project using jquery-1.10.2.js I was activating the form's validation like this:
$.validator.unobtrusive.addValidation("#sch-frm");
and it was working just fine, but now it's submiting the form ignoring the validation.

Related

MVC button save button not working, button does nothing

I am trying to append an item to the SQL table but when I press the save button nothing happens. The reset button works but not the Save. Nothing is showing in the database either. Basically, I don't understand why the buttons act as if there was no code attached to it. No Error message or crash shows up either.
View
#model BudgetAmazon.ViewModel.ItemViewModel
#{
ViewBag.Title = "Item";
}
<h2>Index</h2>
<script type="text/javascript">
$(document).ready(function () {
$("#btnSave").click(function () {
SaveItem();
});
$("#btnReset").click(function () {
ResetItem();
});
});
function ResetItem() {
$("#CategoryId").val("1");
$("#ItemCode").val("");
$("#ItemName").val("");
$("#Description").val("");
$("#ItemPrice").val("0");
$("#ImagePath").val("");
}
function SaveItem() {
var formData = new FormData();
formData.append("CategoryId", $("#CategoryId").val());
formData.append("ItemCode", $("#ItemCode").val());
formData.append("ItemName", $("#ItemName").val());
formData.append("Description", $("#Description").val());
formData.append("ItemPrice", $("#ItemPrice").val());
formData.append("ImagePath", $("#ImagePath").get(0).files[0]);
$.ajax({
async: true,
type: 'POST',
contentType: false,
processDate: false,
data: formDate,
url: '/Item/Index',
success: function (data) {
if (data.success) {
alert(data.Message);
ResetItem();
}
},
error: function (data) {
alert('There is a problem adding items.');
}
});
}
</script>
<div class="container">
<div class="col-md-4">
<div class="form-group">
Category :
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.CategorySelectListItem, "Value", "Text"),
new { #class = "form-control"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Code :
#Html.TextBoxFor(model => model.ItemCode, new { #class = "form-control", autocomplete = "Off"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Name :
#Html.TextBoxFor(model => model.ItemName, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Description :
#Html.TextBoxFor(model => model.Description, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Price :
#Html.TextBoxFor(model => model.ItemPrice, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Image Path :
#Html.TextBoxFor(model => model.ImagePath, new { type = "file", #class = "form-control"})
</div>
</div>
<div>
<input type="button" value="Save" name="save" class="btn btn-primary" id="btnSave"/>
<input type="button" value="Reset" name="reset" class="btn btn-danger" id="btnReset"/>
</div>
</div>
Controller
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BudgetAmazon.Models;
using BudgetAmazon.ViewModel;
namespace BudgetAmazon.Controllers
{
public class ItemController : Controller
{
private BudgetAmazonEntities3 objBudgetAmazonEntities;
public ItemController()
{
objBudgetAmazonEntities = new BudgetAmazonEntities3();
}
// GET: Item
public ActionResult Index()
{
ItemViewModel objItemViewModel = new ItemViewModel();
objItemViewModel.CategorySelectListItem = (from objCat in objBudgetAmazonEntities.Categories select new SelectListItem()
{
Text = objCat.CategoryName,
Value = objCat.CategoryId.ToString(),
Selected = true
});
return View(objItemViewModel);
}
[HttpPost]
public JsonResult Index(ItemViewModel objItemViewModel)
{
string NewImage = Guid.NewGuid() + Path.GetExtension(objItemViewModel.ImagePath.FileName);
objItemViewModel.ImagePath.SaveAs(Server.MapPath("~/Images/" + NewImage));
Item objItem = new Item();
objItem.ImagePath = "~/Images/" + NewImage;
objItem.CategoryId = objItemViewModel.CategoryId;
objItem.Description = objItemViewModel.Description;
objItem.ItemCode = objItemViewModel.ItemCode;
objItem.ItemId = Guid.NewGuid();
objItem.ItemName = objItemViewModel.ItemName;
objItem.ItemPrice = objItemViewModel.ItemPrice;
objBudgetAmazonEntities.Items.Add(objItem);
objBudgetAmazonEntities.SaveChanges();
return Json(new {Success = true, Message = "Item is added Successfully."}, JsonRequestBehavior.AllowGet);
}
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BudgetAmazon.ViewModel
{
public class ItemViewModel
{
public Guid ItemId { get; set; }
public int CategoryId { get; set; }
public string ItemCode { get; set; }
public string ItemName { get; set; }
public string Description { get; set; }
public decimal ItemPrice { get; set; }
public HttpPostedFileBase ImagePath { get; set; }
public IEnumerable<SelectListItem> CategorySelectListItem { get; set; }
}
}
I think the problem is you have made a typo mistake while giving the forma data. Try changing "formDate" to "formData". I have attached the picture to show where to.
enter image description here
As far as I know, your url may require something like .html file extension. Try to revise like url: "/Item/index.html, and see if the request success or not.

ajax mvc html: jquery val not preventing call to api

When clicking a button I am calling a web api with ajax. My form is using JqueryVal, to make form validations, according to my viewmodel data annotations.
My problem is that when I click the button "Listo" in my form, it calls my API, inspite of jqueryval is marking an error( selecting a file is required)
This is my code:
My viewmodel that contains data annotations(the dataannotations are used along with the jquery.validate.js and jquery.validate.unobtrusive.js. As you can see, it is working, but is not preventing the API from being called):
public class CursoViewModel
{
public Guid Id { get; set; }
[MaxLength(125)]
public string Titulo { get; set; }
[Required]
public string Descripcion { get; set; }
[Required(ErrorMessage ="selecciona una imagen para tu curso")]
[DataType(DataType.Upload)]
public HttpPostedFileBase Imagen { get; set; }
}
The class posted to my api
public class person
{
public string name { get; set; }
public string surname { get; set; }
}
The Api code
[HttpPut]
[Route("api/ProfesorCurso/test")]
public string Put(person p)
{
return p.name + p.surname;
}
My View
#model project.ViewModels.CourseViewModel
<form id="Editcurso" method="post" action="#">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "Please fix the following errors.")
<div class="container">
<div class="form-group">
#Html.LabelFor(c=>c.Titulo)
#Html.TextBoxFor(c => c.Titulo, new {id="titulo", #class="form-control"})
#Html.ValidationMessageFor(m => m.Titulo)
</div>
<div class="form-group">
#Html.LabelFor(c => c.Descripcion)
#Html.TextAreaFor(c => c.Descripcion, new {id="descripcion", #class = "form-control" })
#Html.ValidationMessageFor(m => m.Descripcion)
</div>
<div class="thumbnail" id="imagencurso"></div>
<div class="form-group">
#Html.LabelFor(m => m.Imagen)
#Html.TextBoxFor(m => m.Imagen, new {id="imagen" ,type = "file", data_rule_validCustomer = "true" })
#Html.ValidationMessageFor(m => m.Imagen)
</div>
<button id="submiter" type="submit" class="btn btn-primary">Listo!</button>
</div>
</form>
The scripts in the view
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script>
$(document).ready(function () {
$("#submiter").click(function () {
jQuery.support.cors = true;
var person = new Object();
person.name = "Sourav";
person.surname = "Kayal";
$.ajax({
url: '/api/ProfesorCurso/test',
type: 'PUT',
dataType: 'json',
data: person,
success: function (data) {
console.log(data);
return false;
},
error: function (x, y, z) {
alert('error al postear');
return false;
}
});
});
});
</script>
}
What can I do to prevent ajax to call my api when clicking my form button, if there are Jquery validation errors?
thanks
You should be handling the .submit() event of the form, and then your can check .valid(), and if not cancel the ajax call. Note you should also be cancelling the default submit.
$('#Editcurso').submit(e) {
e.preventDefault(); // prevent the default submit
if (!$(this).valid()) {
return; // exit the function and display the errors
}
....
$.ajax({
....
});
}
As a side note, there is no point adding new { id="titulo" } etc - the HtmlHelper methods that generate form controls already add an id attribute based on the property name

jQuery code doesn’t work properly after submitting Ajax.BeginForm in asp.net mvc

I tried to minimize huge problem to a small one so I created the new sample web project; mvc-empty in VS. I created one view named „Index” in Home controller. Index view code:
#model WebApplication16.ViewModels.Home.IndexVM
#{
ViewBag.Title = "Index";
}
#Html.Partial("~/Views/Home/_Orders.cshtml", Model.Orders)
#section scripts{
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
$("#Type").change(function () {
$('#order-current > img').remove();
var currentOrder = "#Type_" + $("#Type").find('option:selected').text();
var $img = $(currentOrder).clone();
$img.removeClass("hidden");
$("#order-current").append($img);
$("#ajax-form").submit();
});
</script>
}
Home controller code:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
IndexVM dataVM = new IndexVM();
GetControlsDataSource(dataVM.Orders);
return View(dataVM);
}
private static void GetControlsDataSource(OrdersVM dataVM)
{
List<SelectListItem> typeControlDataSource = new List<SelectListItem>();
foreach (var en in Enum.GetValues(typeof(TypeEnum)))
{
SelectListItem item = new SelectListItem();
item.Text = en.ToString();
item.Value = ((int)en).ToString();
typeControlDataSource.Add(item);
}
dataVM.TypeControlDataSource = typeControlDataSource;
}
[HttpPost]
public ActionResult Pay(IndexVM dataVM)
{
GetControlsDataSource(dataVM.Orders);
if (ModelState.IsValid)
{
dataVM.Orders.Info = "Info bla bla bla";
return PartialView("~/Views/Home/_Orders.cshtml", dataVM.Orders);
}
else
{
return View(dataVM);
}
}
}
There is also a partial view named “_Orders”, which is rendered on the Index view.The code of _Orders partial view:
#model WebApplication16.ViewModels.Home.OrdersVM
#using (Ajax.BeginForm("Pay", "Home", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result",
}, new { id = "ajax-form" }))
{
<div id="result">
<div id="order-current">
</div>
<div>
#Html.TextBoxFor(x => x.Name, new { #class = "form-control", style = "margin-top:10px;", id = "Name" })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div>
#Html.DropDownListFor(x => x.Type, Model.TypeControlDataSource, new { #class = "form-control", style = "margin-top:10px;", id = "Type", })
#Html.ValidationMessageFor(x => x.Type)
</div>
<div>
<p>#Model.Info</p>
</div>
<button type="submit" class="btn btn-primary" name="ok"> OK</button>
</div>
}
<div id="orders-container">
<img id="Type_I" src="~/App_Images/Type_I.png" class="img-responsive hidden" />
<img id="Type_II" src="~/App_Images/Type_II.png" class="img-responsive hidden" />
<img id="Type_III" src="~/App_Images/Type_III.png" class="img-responsive hidden"/>
</div>
Index model is described by class IndexVM:
public class IndexVM
{
public IndexVM()
{
this.Orders = new OrdersVM();
}
public OrdersVM Orders { get; set; }
}
_Orders model is described by class OrdersVM:
public class OrdersVM
{
[Required]
public string Name { get; set; }
public string Info { get; set; }
[Required]
public TypeEnum Type { get; set; }
public List<SelectListItem> TypeControlDataSource { get; set; }
}
public enum TypeEnum
{
I,
II,
III
}
After change of value in DropDownListFor control with id=”Type”, the picture from hidden field should be injected by jquery code located in Index view into container with id=”order-current” and after that operation the ajax-form should be submitted. It works properly but after calling
return PartialView("~/Views/Home/_Orders.cshtml", dataVM.Orders);
from the HomeController, the field Info is updated properly but the injected picture from the “order-current” div container is gone. I tried to see what’s wrong in Google Chrome using F12 button and there are no errors but appeared an infinite loop in “browserLink” script. I can’t explain why.
All I want is to see the injected picture in container with id=”order-current after submitting the ajax-form. How to do this and what I did wrong?
Thanks to my friend I finally solved the problem. After updating the “result” container, all events added by jQuery to controls located in this container are unpinned. That’s why it crashes.
The way to make it work correctly is to create a function and pin it to the event OnComplete of AjaxBeginForm. After each update of the result container via ajax, this function is called. I also made a small mistake in Home controller because I inserted a wrong view model class. After all changes, it looks like this;
Home controller code:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
IndexVM dataVM = new IndexVM();
GetControlsDataSource(dataVM.Orders);
return View(dataVM);
}
private static void GetControlsDataSource(OrdersVM dataVM)
{
List<SelectListItem> typeControlDataSource = new List<SelectListItem>();
foreach (var en in Enum.GetValues(typeof(TypeEnum)))
{
SelectListItem item = new SelectListItem();
item.Text = en.ToString();
item.Value = ((int)en).ToString();
typeControlDataSource.Add(item);
}
dataVM.TypeControlDataSource = typeControlDataSource;
}
[HttpPost]
public ActionResult Pay(OrdersVM dataVM)
{
GetControlsDataSource(dataVM);
if (ModelState.IsValid)
{
dataVM.Info = "Info bla bla bla";
return PartialView("~/Views/Home/_Orders.cshtml", dataVM);
}
else
{
return View(dataVM);
}
}
}
Index view:
#model WebApplication16.ViewModels.Home.IndexVM
#{
ViewBag.Title = "Index";
}
#Html.Partial("~/Views/Home/_Orders.cshtml", Model.Orders)
<div id="orders-container">
<img id="Type_I" src="~/App_Images/Type_I.png" class="img-responsive hidden" />
<img id="Type_II" src="~/App_Images/Type_II.png" class="img-responsive hidden" />
<img id="Type_III" src="~/App_Images/Type_III.png" class="img-responsive hidden" />
</div>
#section scripts{
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
function imageOnChangeEvent() {
$("#ajax-form").submit();
}
function OnCompleteAjaxForm() {
$('#order-current > img').remove();
var currentOrder = "#Type_" + $("#Type").find('option:selected').text();
var $img = $(currentOrder).clone();
$img.removeClass("hidden");
$("#order-current").append($img);
}
</script>
}
_Orders partial view code:
#model WebApplication16.ViewModels.Home.OrdersVM
<div id="result">
#using (Ajax.BeginForm("Pay", "Home", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result",
OnComplete = "OnCompleteAjaxForm()"
}, new { id = "ajax-form" }))
{
<div id="order-current">
</div>
<div>
#Html.TextBoxFor(x => x.Name, new { #class = "form-control", style = "margin-top:10px;", id = "Name" })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div>
#Html.DropDownListFor(x => x.Type, Model.TypeControlDataSource, new { #class = "form-control", style = "margin-top:10px;", id = "Type", onchange = "imageOnChangeEvent()" })
#Html.ValidationMessageFor(x => x.Type)
</div>
<div>
<p>#Model.Info</p>
</div>
<button type="submit" class="btn btn-primary" id="button_ok" name="ok"> OK</button>
}
</div>

Validation fires but doesn't show message on failure. What am I missing? MVC 5 Razor

I can't seem to get the Validation error messages to show under the input model fields on the View.
The [Required] tag above Description input makes the ModelState Invalid, but doesn't stop the submission. I have to catch it with checking the Model State. Am I missing some .js files? I dont' have any examples to doublecheck this.
Here is my model (notice I have only one [Required] for now):
public partial class Requests
{
public int RequestID { get; set; }
public string NickName { get; set; }
public Nullable<double> Lat { get; set; }
public Nullable<double> Lng { get; set; }
public string ZipCode { get; set; }
[Required(ErrorMessage = "Description of what you need is missing.")]
public string Description { get; set; }
public System.DateTime DateCreated { get; set; }
}
Here is my View where the Description input needs input.
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #rows = "20", #cols = "200" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
Here is my controller ActionResult (skinnied down)
if (ModelState.IsValid)
{
//THIS ALL WORKS IF Description HAS INPUT
}
else
{
TempData["Saved"] = "Nothing saved yet. Look for reason.";
return RedirectToAction("StoreRequests", new { lat = requests.Lat, lng = requests.Lng });
}
On ModelState failure the user is directed to the correct View and TempData shows that nothing was saved. However, there is no error message on the View below the offending input, no ValidationSummary at the top of the view, and submission is not stopped on input mistake.
#if(TempData["Saved"] != null)
{
<span style="color: red;">#TempData["Saved"].ToString()</span>
}
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
In order to get client side validation (and therefore prevent the form being submitted if its invalid), you need to include the following scripts in you view (or layout).
jquery-{version}.js
jquery.validate.js
jquer.validate.unobtrusive.js
If you have the default bundles set up by VS when you create a new project, you can simply add the following to the view
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
In addition, you should not be redirecting if ModelState is invalid, but rather returning the current view, which will display any validation errors even if the user has disabled javascript. By redirecting, your lose current ModelState so no validation errors will be displayed in the view your redirecting to, not to mention that any data the user previously filled (except the 2 parameters your passing) will be lost.
public ActionResult Edit (Requests model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// save you data and redirect
}
Include the following necessary scripts directly in your .cshtml file.
<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

Partial view action resulting in 404 error

I have this code snippet in my view:
#if (User.Identity.IsAuthenticated)
{
#Html.Partial("_PostCommentPartial", new PostCommentViewModel(Model.Id))
}
On my partial view:
#using (Ajax.BeginForm("PostComment", "SecondComments",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.InsertBefore,
UpdateTargetId = "comments"
}))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.TicketId)
#Html.EditorFor(m => m.Content)
<div class="col-md-5">
<input type="submit" class="btn btn-default pull-right" />
</div>
}
My controller:
public class SecondCommentsController : BaseController
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PostComment(PostCommentViewModel comment)
{
// some codes here
}
}
When I'm clicking the submit button, I am getting a 500 error. Please advise.
EDIT My base controller class that was inherited by my controller:
[HandleError]
public class BaseController : Controller
{
protected ITicketSystemData Data { get; private set; }
protected User UserProfile { get; private set; }
public BaseController(ITicketSystemData data)
{
this.Data = data;
}
protected override IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
{
this.UserProfile = this.Data.Users.All().Where(u => u.UserName == requestContext.HttpContext.User.Identity.Name).FirstOrDefault();
return base.BeginExecute(requestContext, callback, state);
}
}

Categories

Resources