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
Related
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.
I have a class in my project which looks like this:
public class MyClass
{
[Required]
public string keyword { get; set; }
public string saleRange { get; set; }
}
This is how the action looks like:
[HttpPost]
[ActionName("Analyze")]
public ActionResult Analyze(MyClass obj)
{
if (!ModelState.IsValid)
{
return Json("error");
}
else
{
return Json("ok");
}
}
And in jQuery:
$(document).on("click",".btnAnalyze",function() {
var data =
{
keyword: $(".keywordInput").val(),
saleRange: "1"
};
console.log($(".keywordInput").val());
$.post("/Controller/Analyze",data).done(function(){
if (data == "error") {
alert("all fields are required");
}
});
});
As you can see I'm mapping a JS object to a C# class object which I pass to my action. Now what the problem here is, even if I don't supply anything for the keyword property (ie. it's null or ""), the ModelState still shows it's property IsValid as true, but it should be set on false if I don't pass anything as keyword parameter.
This is first part of the problem, the other question I have here is how would I, If I have 10 textbox field, of which all are required. And if user enters only 1/10 of those, I would like to send a message for the first next field user has to validate in order to proceed, like "texbox #2 is required, please enter something" instead of a just general message like "all fields are required" or something like that ?
Can someone help me out with this ?
Why does the modelstate shows the it's valid even though after I dont' supply keyword parameter in the object?
#T.Jung here Is a sample of input:
<input placeholder="Enter keywords" class="form-control keywordInput" type="text" style="width:80%">
It's basically just a plain input field in html..
You can use form validation, which will perform data annotation validation on the client side and you do not have to go to the server side.
$('#btnAnalyze').on('click', function (evt) {
if ($('#YourformId').valid()) {
//Perform AJAX call
}
This way if any of the validations do not work, the form.valid() will throw the data annotation errors. So you can add Required to those fields in the models and it will ensure data is not sent if validation fails.
I would do the following:
Make sure your script bundle includes the following 2 files after your jquery and in this order:
jquery.validate.js
jquery.validate.unobtrusive.js
Create your partial / view for your form:
#model MyClassObject
<form action="/action" method="post" id="form">
<div class="row">
#Html.ValidationMessageFor(m => m.Param1)
#Html.LabelFor(m => m.Param1, new { #class = "label" })
#Html.TextBoxFor(m => m.Param1, new { #class = "textbox" })
</div>
<div class="row">
#Html.ValidationMessageFor(m => m.Param2)
#Html.LabelFor(m => m.Param2, new { #class = "label" })
#Html.TextBoxFor(m => m.Param2, new { #class = "textbox" })
</div>
<div class="row">
#Html.ValidationMessageFor(m => m.Param3)
#Html.LabelFor(m => m.Param3, new { #class = "label" })
#Html.TextBoxFor(m => m.Param3, new { #class = "textbox" })
</div>
<input type="submit" value="submit">
</form>
Do you server side validation
[HttpPost]
[ActionName("action")]
public ActionResult action(MyClassObject obj)
{
if(!ModelState.IsValid)
{
return action(); // this is your original action before you do the post
}
else
{
// do your processing and return view or redirect
return View(); // or redirect to a success page
}
}
Add the error message to your class:
public class MyClassObject
{
[Required(ErrorMessage = "Please enter a keyword")]
public string keyword { get; set; }
public string saleRange { get; set; }
}
Your js validation will be plumbed in now but you will need to stop the form from posting and check the validation before doing your ajax request:
var form = document.getElementById('form'), // get a normal js form (I do this just so I can pass in the method and action dynamically - seems better than using .attr() to get them
jqForm = $(form);
jqForm.on('submit', function(e) {
e.preventDefault();
if (jqForm.valid()) {
var formData = jqForm.serializeArray(); // I use serializeArray so I can add extra values if I need to, you can use serialize()
// formData.push({ name: this.name, value: this.value }); - optional for adding extra data
$.ajax({
url: form.action, // this can be just a specific json url if you just need to get some json - it doesn't have to be the same url as the fallback (no js url)
type: form.method,
data: formData,
success: function(result) {
// do success stuff!
}
});
}
})
You should stringify the JavaScript Object in the Jquery post
JSON.stringify(data)
If you want to do the Validation on server side:
Contoller:
[HttpPost]
[ActionName("Analyze")]
public ActionResult Analyze(Model viewModel)
{
if(viewModel.PropertyName.IsNullOrEmpty()){
{
ModelState.AddModelError("PropertyName", "The Field InputFieldName neeeds to be filled!");
}
if (ModelState.IsValid)
{
//Do what ever you want with your Inofrmation
//return a redirct or anything else
}
//If you got here some fields are not filled
return View(viewModel);
}
View:
#Html.TextBoxFor(x => x.PropertyName)
#Html.ValidationMessageFor(m => m.PropertyName, "", new { #class = "text-danger" })
//#class = "text-danger" only if you're using bootstrap
I do all of my validation this way.
I am new to both asp.net MVC and JQuery so be gentle.
I am trying to use a HTTP Post to update my contact form, used to send an email, using AJAX. I have seen lots of posts but what I want seems specific and I cant seem to find anything relevant.
The down low: I have a layout page which has the header, renders the body and has my footer in. My footer contains the form I want to submit. I want to submit this form without refreshing the whole page. The layout page:
<div id="footer">
#{Html.RenderAction("Footer", "Basic");}
</div>
<p id="p"></p>
I have a model for this form to send an email.
namespace SimpleMemberShip.Models
{
public class EmailModel
{
[Required, Display(Name = "Your name")]
public string FromName { get; set; }
[Required, Display(Name = "Your email"), EmailAddress]
[StringLength(100, ErrorMessage = "The email address entered is not valid")]
public string FromEmail { get; set; }
[Required]
public string Message { get; set; }
}
The footer:
<h2> footer yo !</h2>
#Html.ValidationSummary()
<fieldset>
<legend>Contact Me!</legend>
<ol>
<li>
#Html.LabelFor(m => m.FromEmail)
#Html.TextBoxFor(m => m.FromEmail)
</li>
<li>
#Html.LabelFor(m => m.FromName)
#Html.TextBoxFor(m => m.FromName)
</li>
<li>
#Html.LabelFor(m => m.Message)
#Html.TextBoxFor(m => m.Message)
</li>
</ol>
<button id="submit"> Submit </button>
</fieldset>
controller:
[ChildActionOnly]
public ActionResult Footer()
{
return PartialView("~/Views/Shared/_Footer.cshtml");
}
[HttpPost]
public ActionResult Footer(EmailModel model)
{
return PartialView("~/Views/Shared/_Footer.cshtml");
}
I want to use the model validation and everything to be the same or similar as if the form was posted normally through the server.
Edit:
My new code, which works great! but it only works once, when the button is clicked again nothing happens. Anyone know why?
<script type="text/javascript">
$("#submit").click(function () {
$("#footer").html();
var url = '#Url.Action("Footer", "Basic")';
$.post(url, { FromName: $("[name=FromName]").val(), FromEmail: $(" [name=FromEmail]").val(), Message: $("[name=Message]").val() }, function (data) {
$("#footer").html(data);
});
var name = $("[name=FromName]").val();
$("#p").text(name);
});
</script>
new Edit:
did some research and using
$("#submit").live("click",function () {
instead of
$("#submit").click(function () {
seemed to do the trick.
<script type="text/javascript">
$("#submit").live("click",function () {
$('.validation-summary-errors').remove();
var url = '#Url.Action("Footer", "Basic")';
$.post(url, { FromName: $("[name=FromName]").val(), FromEmail: $("[name=FromEmail]").val(), Message: $("[name=Message]").val() }, function (data) {
$("#footer").html(data);
});
});
</script>
ended up with this but will try the "serialize()" option next time.
controller was changed to this without the [ChildActionOnly] and works perfect now
[HttpPost]
public ActionResult Footer(EmailModel model)
{
return PartialView("~/Views/Shared/_Footer.cshtml");
}
Thank you everyone that helped!
Change the [ChildActionOnly] to [HttpGet] in the controller
You can pass model data to controller by doing the following steps
1. Get the input values on click of submit and sent to the Footer action in controller
$("#submit").click(function () {
var FromEmailValue = $('#FromEmail').val();
var FromNameValue = $('#FromName').val();
var MessageValue = $('#Message').val();
var url = '#Url.Action("Footer", "Basic")';
$.ajax({
url: urlmodel,
data: { FromName: FromNameValue, FromEmail: FromEmailValue, Message: MessageValue},
cache: false,
type: "POST",
success: function (data) {
do something here
}
error: function (reponse) {
do something here
}
});
});
In the controller
``
[HttpGet]
public ActionResult Footer()
{
return PartialView("~/Views/Shared/_Footer.cshtml");
}
[HttpPost]
public ActionResult Footer(string FromName = "", string FromEmail = "", string Message = "")
{
//for ajax request
if (Request.IsAjaxRequest())
{
do your stuff
}
}
NPG_Chemical_Measurement_Methods is an ICollection type. In my Chemical.cshtml, I have:
<div id="nioshs">
#Html.EditorFor(model => model.NPG_Chemical_Measurement_Methods)
</div>
and in the EditorTemplate view:
<div class="method" style="display:inline-block;">
<p>
#Html.RemoveLink("x", "div.method", "input.mark-for-delete")
#Html.HiddenFor(x => x.DeleteMethod, new { #class = "mark-for-delete" })
#Html.TextBoxFor(x => x.Measurement_Method)
#Html.ValidationMessageFor(model => model.Measurement_Method, "", new { #class = "text-danger" })
#Html.Hidden("Measurement_Type", "NIOSH")
</p>
</div>
I want to have something like when I give input for #Html.TextBoxFor(x => x.Measurement_Method), then click on other place of the current page, an alert will popup says Not exist in Database if record cannot be found in Measurement_Method table.
NPG_Chemical.cs has:
public partial class NPG_Chemical
{
public NPG_Chemical()
{
this.NPG_Chemical_Measurement_Methods = new HashSet<NPG_Chemical_Measurement_Method>();
}
[StringLength(256)]
[Remote("IsUserExists", "NPG_Chemical", ErrorMessage = "Chemical Name already in use")]
public string Chemical { get; set; }
public virtual ICollection<NPG_Chemical_Measurement_Method> NPG_Chemical_Measurement_Methods { get; set; }
internal void CreateMeasurementMethods(int count = 1)
{
for (int i = 0; i < count; i++)
{
NPG_Chemical_Measurement_Methods.Add(new NPG_Chemical_Measurement_Method());
}
}
Measurement_Method.cs has:
public partial class NPG_Chemical_Measurement_Method
{
[StringLength(256)]
[Remote("IsNIOSHExists", "NPG_Chemical", ErrorMessage = "NIOSH number does not exist")]
public string Measurement_Method { get; set; }
}
NPG_ChemicalController has:
public JsonResult IsUserExists(string Chemical)
{
return Json(!db.NPG_Chemical.Any(x => x.Chemical == Chemical), JsonRequestBehavior.AllowGet);
}
public JsonResult IsNIOSHExists(string Measurement_Method)
{
System.Diagnostics.Debug.WriteLine("value:",Measurement_Method);
return Json(db.NPG_NIOSH_Method.Any(x => x.Measurement_Number == Measurement_Method), JsonRequestBehavior.AllowGet);
}
This hopefully will get you close. I'm just writing this from memory. But basically one way to do this is handle an onblur event of your textbox with a javascript function. Then do an ajax call to a controller sending the value of Measurement_Method, validate the data and return true or false. If false show an alert box. You'll need to include the jquery library to use this.
#Html.TextBoxFor(x => x.Measurement_Method, new {onblur = "Validate()"})
Then javascript
function Validate() {
$.ajax({
url: "#Url.Action("CheckTextField", "Controller")\?value=" + $('#Measurement_Method').val(),
dataType: "html",
success: function(data) {
if (data == "false")
{
alert('Not exist in Database');
}); }
Your controller
public string CheckTextField(string value)
{
//validate the value here
return "true" or "false"
}
I have JS function that is basically
<script type="text/javascript">
function doSomething() {
var s = 'some data'
return s; }
</script>
and
#using (Html.BeginForm(new { data_to_send = x))
{
//some form controls that are sent to controller via model
}
Is it possible, and how, to assign value returned by doSomething function to x variable in form?
I don't need x in my model, because it won't go to database. It's just some additional info from user, how to manipulate data in model before saving to database.
edit: Controller action is
public actionresult MyController(string data_to_Send, model) {}
In the View:
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.X1)
#Html.HiddenFor(model => model.X2)
}
In the Model:
public class YourModel
{
public string X1 { get; set; }
public string X2 { get; set; }
}
In the Controller:
[HttpPost]
public ActionResult Index(YourModel model)
{
string x1 = model.X1;
string x2 = model.X2;
return View(model);
}
The form has to be posted in order to do what you are looking for.
You can post the model along with PostedDateValue
#using (Html.BeginForm("ActionMethod", "Controller", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-field required">
<label for="Posted Data">Posted Data</label>
<input id="txtPostedData" name="PostedDateValue" >
<input type="submit" class="gradient" value="SUBMIT" />
</div>
}
)
Controller
public ActionResult ActionMethod(string PostedDateValue)
{
}