I've a dynamic form (bootstrap modal) where I want to use data validation.
Therefor when the modal is being shown I apply the validator in my a script.
This is my jquery/javascript code in the index page for showing the modal
$("#btnCreate").on("click", function (e) {
// hide dropdown if any
$(e.target).closest('.btn-group').children('.dropdown-toggle').dropdown('toggle');
$('#myModalContent').load(this.href, function () {
$('#myModal').modal({
/*backdrop: 'static',*/
keyboard: true
}, 'show');
$('#myModal').on('shown.bs.modal', function () {
$('.chzn-select', this).chosen({ width: "inherit", disable_search: true });
/*$("form").data("validator", null);
$.validator.unobtrusive.parse($("form"));*/
var form = $("form") //use more specific selector if you like
form.removeData("validator").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(form);
});
bindForm(this);
});
return false;
});
function bindForm(dialog) {
$('form', dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$('#myModal').modal('hide');
//Refresh
location.reload();
} else {
$('#myModalContent').html(result);
bindForm();
}
}
});
return false;
});
The validation works, but the problem is as follows:
So when I click on the submit button, without the required fields filled in the form still submits. Instead of blocking the POST and telling the user something is wrong.
This is my create view:
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { #class="horizontal-form"})) {
#Html.ValidationSummary(false)
<div class="modal-body">
<fieldset>
<div class="form-group">
<div class="editor-label">
#Html.LabelFor(model => model.Naam)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Naam, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Naam)
</div>
</div>
<div class="form-group">
<div class="editor-label">
#Html.LabelFor(model => model.Omschrijving)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Omschrijving, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Omschrijving, "", new { #class = "text-danger" })
</div>
</div>
<div class="modal-footer">
<input type="submit" id="verstuurFormulier" value="Create" class="btn btn-primary" />
</div>
</fieldset>
</div>
}
You're not stopping the normal submit from happening:
function bindForm(dialog) {
$('form', dialog).submit(function (e) {
e.preventDefault();
$.ajax({
...
This means that you have to manually decide when to submit though. I use the following in some of my projects:
$('#myForm').removeData("validator");
$.validator.unobtrusive.parse($('#myForm'));
if ($('#myForm').valid()) {
$('#myForm').submit();
}
return false;
I think the problem might lie in the line:
$("form").data("validator", null);
I usually use this little snippet (found ofc on StackOverflow, but I do not remember the person to give credit to :( )
(function ($) {
$.validator.unobtrusive.parseDynamicContent = function (selector) {
//use the normal unobstrusive.parse method
$.validator.unobtrusive.parse(selector);
//get the relevant form
var form = $(selector).first().closest('form');
//get the collections of unobstrusive validators, and jquery validators
//and compare the two
var unobtrusiveValidation = form.data('unobtrusiveValidation');
var validator = form.validate();
$.each(unobtrusiveValidation.options.rules, function (elname, elrules) {
if (validator.settings.rules[elname] == undefined) {
var args = {};
$.extend(args, elrules);
args.messages = unobtrusiveValidation.options.messages[elname];
//edit:use quoted strings for the name selector
$("[name='" + elname + "']").rules("add", args);
} else {
$.each(elrules, function (rulename, data) {
if (validator.settings.rules[elname][rulename] == undefined) {
var args = {};
args[rulename] = data;
args.messages = unobtrusiveValidation.options.messages[elname][rulename];
//edit:use quoted strings for the name selector
$("[name='" + elname + "']").rules("add", args);
}
});
}
});
}
})($);
Then just delete the null on form validator and change the parse call to:
$.validator.unobtrusive.parseDynamicContent('form');
Hope this helps.
Related
I have written a html code for cshtml which consists two button save and update:
<div class="modal-body">
#using (Html.BeginForm("Create", "CustomActivity", FormMethod.Post, new { role = "form", #id = "customActivityForm", #class = "activityForm" }))
{
<div>
<div class="container" id="activity">
#Html.LabelFor(m => m.AdminCustomActivity.Activity, new { #class = "" })
#Html.TextBoxFor(m => m.AdminCustomActivity.Activity, new { #class = "w100p mb0" })
#Html.ValidationMessageFor(m => m.AdminCustomActivity.Activity, "", new { #class = "text-danger" })
</div>
<div class="container">
<div class="column one-fourth">
<div>
#Html.LabelFor(m => m.AdminCustomActivity.Rate, new { #class = "wcrate" })
#Html.TextBoxFor(m => m.AdminCustomActivity.Rate, new { #class = "w100p mb0" })
#Html.ValidationMessageFor(m => m.AdminCustomActivity.Rate, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="modal-footer">
#Html.HiddenFor(m => m.AdminCustomActivity.WorkOrderId, new { #id = "hfWorkOrderId" })
#Html.HiddenFor(m => m.AdminCustomActivity.Id, new { #id = "customActivityId" })
<button type="submit" class="btn btn-primary">Create</button>
<button class="btn btn-primary" id="updateActivity">Update</button>
</div>
</div>
}
</div>
Now in the footer there is two button add and update the validation works perfect for add button but the validation dosen't works for Update button since the function call of update button is from jquery
$("#updateActivity").click(function () {
event.preventDefault();
$.ajax({
type: "POST",
url: "/CustomActivity/Update",
data: {
WorkOrderId: #Model.Id,
},
success: function () {
location.reload();
window.scrollTo(0, 0);
},
error: function () {
//alert(erro.data)
}
});
})
How can I allow to check validation in update button same as add bututon. I tried using type='submit' but that doesn't works
You would have to call a function which implements the jQuery validatation to show validation messages at the Update button click:
function validateUpdate() {
$("#customActivityForm").validate({
rules: {
AdminCustomActivity_Activity: "required",
AdminCustomActivity_Rate: "required"
}
});
}
$("#updateActivity").click(function () {
validateUpdate();
$.ajax({
type: "POST",
url: "/CustomActivity/Update",
data: {
WorkOrderId: #Model.Id,
},
success: function () {
location.reload();
window.scrollTo(0, 0);
},
error: function () {
//alert(erro.data)
}
});
})
I can easily show/hide a <div> based on this View code:
<div class="form-group">
#Html.LabelFor(m => m.countryID, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.countryID, ((IEnumerable<Corporate.Models.Country>) ViewBag.Possiblecountries).OrderBy(c => c.countryName).Select(option => new SelectListItem
{
Text = Html.DisplayTextFor(_ => option.countryName).ToString(),
Value = option.countryID.ToString(CultureInfo.InvariantCulture),
Selected = (Model != null) && (option.countryID == Model.countryID)
}), new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.countryID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="vatNumberDiv">
#Html.LabelFor(m => m.vatNumber, new {#class = "col-md-2 control-label"})
<div class="col-md-10">
#Html.TextBoxFor(m => m.vatNumber, new {#class = "form-control"})
#Html.ValidationMessageFor(m => m.vatNumber, "", new {#class = "text-danger"})
</div>
</div>
and based on this Script:
<script type="text/javascript">
$(function () {
$('#countryID').change(function () {
var value = $(this).val();
if (value == 'FRA') {
$('#vatNumberDiv').show();
} else {
$('#vatNumberDiv').hide();
}
});
});
</script>
but what about checking all the EU members? I have a method called bool IsMemberEU() that requires MVC context to execute. Can I call it inside the script?
Maybe it's better to generate by code all the options inside the script? Something like:
if (value == 'FRA' ||
value == 'DEU' ||
value == 'ITA' ||
...
...
) {
Do I have some other option?
Thanks.
EDIT:
This is the code I need, to check if the country is EU member:
foreach(Country c in context.Countries)
{
if (IsMemberEU(c))
{
// is EU memeber
}
}
EDIT2: For M12 Bennet
<script type="text/javascript">
// $(function () {
$(document).ready(function() {
$('#countryID').change(function () {
// get selected option to submit to method IsMemberEU
var selectedOption = $(this).val();
// create URL for ajax call
var ajaxUrl = '#Url.Action("IsMemberEU", "Customers")';
$.ajax({
url: ajaxUrl,
data: { countryAbbv: selectedOption },
success: function(result) {
if (result) {
$("#vatNumberDiv").show();
} else {
$("#vatNumberDiv").hide();
}
// show result of ajax call in the `p` element on page. This is just testing to see if ajax call worked.
// this can be done with console.log(result) as well.
$("#ShowResult").text(result);
},
error: function(xhr, status, error) {
console.log(xhr);
}
});
});
});
</script>
In a controller, you could create the method IsMemberEU(string countryAbbv). It needs to accept a parameter because you're checking against what you're sending to the method. So your code could look like this:
Controller Method
public bool IsMemberEU(string countryAbbv)
{
var lstCountries = db.Countries.Where(x => x.isEU).Select(t => t.Abbr).ToList();
return lstCountries.Contains(countryAbbv);
}
Then on your Razor/HTML page:
Razor/HTML
<div>
<select id="CountrySelect" name="countryAbbv">
<option value="">-- Select Country --</option>
<option value="FRA">FRA</option>
<option value="DEU">DEU</option>
<option value="ITA">ITA</option>
<option value="USA">USA</option>
</select>
<p id="ShowResult"></p>
</div>
Then in your jQuery to include AJAX:
jQuery/AJAX
<script>
$(document).ready(function() {
// create event listener for change of select
$("#CountrySelect").change(function() {
// get selected option to submit to method IsMemberEU
var selectedOption = $(this).val();
// create URL for ajax call
var ajaxUrl = '#Url.Action("IsMemberEU", "Home")';
$.ajax({
url: ajaxUrl,
data: { countryAbbv: selectedOption },
success: function(result) {
if (result)
$("#vatNumberDiv").show();
else
$("#vatNumberDiv").hide();
},
error: function(xhr, status, error) {
console.log(xhr);
}
});
});
});
</script>
This is working as expected on my end. Now, this is just a basic example that I created myself based off of the information that was provided in the question. You shouldn't have to worry about the HTML/Razor that I provided because you're using Razor syntax.
I'm having a basic form with few text fields and a file upload controller on a bootstrap modal dialog (Bootstrap 4). below is my code:
Model:
public class DemoContent
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[RegularExpression("([0-9]+)",ErrorMessage = "Age must be numbers only")]
public int Age { get; set; }
[EmailAddress]
public string Email { get; set; }
[DataType(DataType.Upload)]
[Display(Name = "Image")]
public HttpPostedFileBase ImageUrl { get; set; }
}
JavaScript
$(function() {
$("a[data-modal=demoPopup]").on("click", function () {
$("#demoModalContent").load(this.href, function () {
$("#demoModal").modal({ keyboard: true }, "show");
$("#demoForm").submit(function () {
if ($("#demoForm").valid()) {
var files = $("ImageUrl").get(0).files;
var data = $(this).serialize();
data.append("ImageUrl", files[0]);
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$("#demoModal").modal("hide");
location.reload();
} else {
$("#MessageToClient").text(result.message);
}
},
error: function () {
$("#MessageToClient").text("The web server had an error.");
}
});
return false;
}
});
});
return false;
});
Controller:
[HttpPost]
public ActionResult Create(DemoContent model)
{
if (model.Age > 55)
{
var file = model.ImageUrl;
return Json(new { success = true });
}
else
{
return Json(new { success = false,message="Invalid Data" });
}
}
Now when i open the popup it works also when i submit the form it goes to the controller along with the file. but the problem is once the server returns the success message the popup shows that message in a blank page instead of capturing it and refreshing the current page or showing the messages. any idea why is this happening.
link to source files : https://drive.google.com/open?id=1W3H3kFEpHJWfaf7_UnJI3O5I900GxyC7
May be you wrote your javascripts function in document.ready() function,That is why it again refreshing.
Write your JavaScript code as follows:
$(function() {
$("a[data-modal=demoPopup]").on("click", function () {
$("#demoModalContent").load(this.href, function () {
$("#demoModal").modal({ keyboard: true }, "show");
$("#demoForm").submit(function (event) { // Pass the event as parameter to the function.
event.preventDefault(); // I have added these two lines
event.stopImmediatePropagation();
if ($("#demoForm").valid()) {
var files = $("ImageUrl").get(0).files;
var data = $(this).serialize();
data.append("ImageUrl", files[0]);
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$("#demoModal").modal("hide");
location.reload();
} else {
$("#MessageToClient").text(result.message);
}
},
error: function () {
$("#MessageToClient").text("The web server had an error.");
}
});
return false;
}
});
});
return false;
});
I think you should install and use Microsoft.Unobtrusive.Validation and *.Ajax, if you want your modal to be updated (I get your question like that...). With this, you can use code like the following example, which can update your modal (used this in a project a few days ago):
Modal:
#using (Ajax.BeginForm("Login", new { Controller = "Home", area = "" }, new AjaxOptions() { OnSuccess = "onSuccessLogin", HttpMethod = "POST", UpdateTargetId = "loginmodalbody"}, new { id = "loginForm" }))
{
<div class="modal-body" id="loginmodalbody">
<div class="text-danger loginfailed"></div>
<div class="container">
<div class="card border-primary mb-3" style="margin: 0 auto;">
<div class="card-body">
#Html.Partial("~/Views/Shared/Modals/LoginModalBody.cshtml")
</div>
</div>
<div class="container">
<span><a onclick="alert('Leads to pw info')" href="#">Forgot password?</a></span>
</div>
<br />
<button class="btn btn-primary btn-block buttonlogin">Login</button>
</div>
<br />
</div>
}
Modal Body:
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-lg-12 col-12">
#Html.EditorFor(model => model.EMail, new { htmlAttributes = new { #class = "form-control", placeholder = "EMail", #id = "inputemail" } })
#Html.ValidationMessageFor(model => model.EMail, "", new { #class = "text-danger", #id = "dangeremail" })
</div>
</div>
<div class="form-group">
<div class="col-lg-12 col-12">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #class = "form-control", placeholder = "Passwort", #id = "inputpassword" } })
#Html.ValidationMessageFor(model => model.Password, "", new { #class = "text-danger", #id = "dangerpassword" })
</div>
</div>
</div>
Thus, it updates your modal body after getting data from the posting of the form - you define the id to be updated within the AjaxOptions, as shown in the above snippet.
I have a Room record in my database and I want to edit it using a JsonResult Edit method in RoomController like this:
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Edit(RoomViewModel roomViewModel)
{
if (roomViewModel == null) throw new ArgumentNullException(nameof(roomViewModel));
try
{
var apartmentRoomViewModel = new ApartmentRoomViewModel
{
Id = _entities.ApartmentRoom.Where(x => x.RoomID == roomViewModel.Id).Select(x => x.Id).Single(),
ApartmentID = _entities.ApartmentRoom.Where(x => x.RoomID == roomViewModel.Id).Select(x => x.ApartmentID).Single(),
RoomID = roomViewModel.Id
};
apartmentRoomViewModel.ApartmentID = roomViewModel.SelectedApartmentID;
var apartmentRoom = AutoMapper.Mapper.Map<ApartmentRoom>(apartmentRoomViewModel);
_entities.ApartmentRoom.AddOrUpdate(apartmentRoom);
_entities.SaveChanges();
var room = AutoMapper.Mapper.Map<Room>(roomViewModel);
var status = _roomRepository.Update(room);
_roomRepository.Save();
return Json(new { status, message = "Success!", url = Url.Action("List", "Room") });
}
catch
{
return Json(new { status = false, message = "Error!" });
}
}
After the method works, edit is successful but I cannot redirect the page to /Room/List. Instead, I am encountering a page like this:
My Script
<script type="text/javascript">
$(document).ready(function () {
$("#RoomEdit").click(function (e) {
e.preventDefault();
var data = {
DoorNumber: $("#DoorNumber").val(),
FloorNumber: $("#FloorNumber").val(),
Capacity: $("#Capacity").val(),
SelectedApartmentID: $("#SelectedApartmentID option:selected").val()
}
$.ajax({
type: "POST",
url: '#Url.Action("Edit","Room")',
dataType: "json",
data: JSON.stringify(data),
contentType: "application/json",
success: function (result) {
if (result.status) {
window.location.href = result.url;
}
},
error: function () {
}
});
return false;
});
});
Edit.cshtml
<div class="row">
<div class="col-md-10 offset-md-1">
<div class="box">
<div class="box-header">
<h2>#ViewBag.Title</h2>
</div>
<div class="box-divider m-a-0"></div>
<div class="box-body">
#using (Html.BeginForm("Edit", "Room", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group row">
#Html.LabelFor(x => x.DoorNumber, new { #class = "col-sm-2 form-control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(x => x.DoorNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.DoorNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group row">
#Html.LabelFor(x => x.FloorNumber, new { #class = "col-sm-2 form-control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(x => x.FloorNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.FloorNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group row">
#Html.LabelFor(x => x.Capacity, new { #class = "col-sm-2 form-control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(x => x.Capacity, new { #class = "form-control" })
#Html.ValidationMessageFor(x => x.Capacity, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group row">
#Html.LabelFor(x => x.ApartmentName, new { #class = "col-sm-2 form-control-label" })
<div class="col-sm-10">
#Html.DropDownListFor(x => x.SelectedApartmentID, Model.ApartmentList, new { #class = "form-control", id = "SelectedApartmentID" })
</div>
</div>
<div class="form-group row m-t-md">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" id="RoomEdit" class="btn green">Düzenle</button>
</div>
</div>
}
</div>
</div>
</div>
I couldn't understand what is wrong with my code. Any help will be appreciated.
Make your Button first with type="Button" instead of Submit, also change the click function id from btnAdd to btnEdit.
At server side, roomViewModel.Id will be getting 0 if you using old method, instead of this do serialize so you can get all the Inputs at server side method.
Also use, #Html.HiddenFor(x => x.id) to pass the Id to Method.
Try this function so you can call your Method with AJAX,
<script type="text/javascript">
$(document).ready(function () {
$("#RoomEdit").click(function (e) {
e.preventDefault();
var data = $("#formName").serialize();
$.ajax({
type: "POST",
url: '#Url.Action("Edit", "Room")',
data: data,
success: function (result) {
if (result.status) {
alert(result.message);
setTimeout(function () {
window.location.href = result.url;
}, 1000);
}
}
});
});
})
</script>
You have code to do ajax submit. But from the image you shared, it looks like it is doing a normal form submit. Make sure that you are preventing the default form submit behavior when the button is clicked.
You already have return false; which should do it.
It should work as long as you do not have other script errors in the page. (you can verify this by opening up the browser console)
Also make sure that you are returning true as the value of status property of the json data you are returning. There is no need to specify JsonRequestBehavior.AllowGet enum in the Json method overload when you are returning from an HttpPost action method. It is needed if your action method is HttpGet
return Json(new { status= true, message = "Success!", url = Url.Action("List", "Room") });
Also, it does not make any sense to have the $.notify call after you redirect to the new page. That means that call will not be executed at all!
When "Other" is selected from the DDL all I want is for the textbox to appear. However it always displays instead of being hidden until called.
My view markup is:
<div class="form-group">
#Html.LabelFor(model => model.SelectType, "Select Type", new { #class = "control-label col-md-5" })
<div class="col-md-1">
#Html.DropDownList("SelectType", null, new { #id = "Other" })
#Html.TextBoxFor(model => model.OtherSpecify, new { #id = "OtherSpecify" })
#Html.ValidationMessageFor(model => model.SelectType)
</div>
I tried the following two javacript codes without any success
<script>
document.addEventListener("DOMContentLoaded", function () {
$("SelectType").trigger("change");
})
$("#SelectType").on("change", function () {
if ($("#SelectType option:selected").val() == 3) {
$("#OtherSpecify").hide();
} else {
$("#OtherSpecify").show();
}
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () { $("SelectType").trigger("change");
})
$(function () {
$('.OtherSpecify').show();
$("Other").change(function () {
if ($(this).is(":selected")) {
$(this).parent().next().hide();
}
else {
$(this).parent().next().show();
}
});
})
</script>
First you shoud check how jQuery selectors work.
In HTML above '$("#SelectType")' - is your select and $("#OtherSpecify") is your textbox.
If you are using jQuery you shoud use it all the time.
Use $(handler) insted of DOMContentLoaded event:
<div class="form-group">
<div class="col-md-1">
#Html.DropDownList("SelectType", new List<SelectListItem> {
new SelectListItem{Text = "test 1", Value = "1"},
new SelectListItem{Text = "test 2", Value = "2"},
new SelectListItem{Text = "Other", Value = "3"}
}, new { #id = "SelectType" })
#Html.TextBox("OtherSpecify", "")
</div>
</div>
#section Scripts {
<script>
$(function() {
$("#SelectType").on("change", function() {
if (parseInt($("#SelectType").val()) == 3) {
$("#OtherSpecify").show();
} else {
$("#OtherSpecify").hide();
}
});
$("#SelectType").trigger("change");
});
</script>
}
Remember to place script after jQuery library is loaded. In most cases #section Scripts do the work.
I have to adjust a few things to enable the Javascript to work. Firstly I seperated out my HTML helpers:
<div class="form-group">
#Html.LabelFor(model => model.SelectType, "Select Type", new { #class = "control-label col-md-5" })
<div class="col-md-1">
#Html.DropDownList("SelectType", String.Empty)
#Html.ValidationMessageFor(model => model.SelectType)
</div>
</div>
<div class="form-group" id="OtherSpecifyFormGroup">
#Html.LabelFor(model => model.OtherSpecify, new { #class = "control-label col-md-4" })
<div class="col-md-4 sbchanged">
#Html.TextBoxFor(model => model.OtherSpecify)
#Html.ValidationMessageFor(model => model.OtherSpecify)
</div>
</div>
Then wrote the following JavaScript code:
<script>
$(document).ready(function () {
//this line fires no matter what
$("#OtherSpecifyFormGroup").hide();
$("#SelectType").change(function () {
var value = document.getElementById("SelectType").value;
if (value == "4") {
$("#OtherSpecifyFormGroup").show("highlight", { color: "#7FAAFF" }, 1000);
}
else {
$("#OtherSpecifyFormGroup").hide();
}
});
})
</script>
I gave my form group for Other Specify an ID so that I could initially hid the textbox. Then declared the variable "value" as in my database the values that populate the DDL have SelectType Ids, therefore it wouldn't call "Other" as it wasn't recognised but as shown when the value "4" is called it works! The else ensures that if any other DDL value is selected then the textbox is hidden again.