Jquery on change function not firing in asp.net MVC
Disclaimer: This might seem like a usual issue up front, but please bear with me and read to the end.
I have two files, namely, CreateProject.cshtml and projectMasterNew.js
CreateProject.cshtml
<div class="form-group">
#Html.LabelFor(model => model.Account.AccountName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Account.AccountName, new SelectList(ViewBag.GetAccount, "AccountId", "AccountName"))
#Html.ValidationMessageFor(model => model.Account.AccountName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Account.DMName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Account.DMName, new SelectList("", "DMId", "DMName"))
#Html.ValidationMessageFor(model => model.Account.DMName, "", new { #class = "text-danger" })
</div>
</div>
<script src="~/Scripts/DMS/projectMasterNew.js"></script>
<script>
var getDmUrl = '#Url.Action("GetDMList", "Project", new { area = "Masters" })';
</script>
projectMasterNew.js
$(document).ready(function () {
alert($("#Account_AccountName").val());
$("#Account_AccountName").on("change", function () {
alert($("#Account_AccountName").val());
var jsonData = { account: $(this).val() };
getJsonCall("Project Creation", getDmUrl, jsonData, function (response) {
var dName = $('#Account_DMName');
alert("Dropdwn update");
});
alert("Success");
}, false);
});
The above jquery code worked until this morning.
Problem:
The script was being accessed at the time of page load, since I got the alert before the function. But the change event did not trigger on when I changed the value in the AccountName dropdown.
I tried calling the change method as follows:
1. $(document).on("change", "#Account_AccountName", function () {
2. $("#Account_AccountName").change(function () {
to no outcome.
So, after a little research, I implemented the below
function DmList() {
alert($("#Account_AccountName").val());
var jsonData = { account: $("#Account_AccountName").val() };
getJsonCall("Project Creation", getDmUrl, jsonData, function (response) {
var dName = $('#Account_DMName');
$.each(response, function (index, item) {
dName.append($('').text(item.DMName).val(item.DMId));
});
alert("Dropdwn update");
});
alert("Success");
}
#Html.DropDownListFor(model => model.Account.AccountName, new SelectList(ViewBag.GetAccount, "AccountId", "AccountName"), new { onchange = "DmList()"})
As you'll notice, the jquery function isn't called on Document Ready. That is because, adding that also threw an error
For those wondering, my BundleConfig.cs has
bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js"));
and my _Layout.cshtml has
#Scripts.Render("~/bundles/jquery")
I would like to know why this was an issue and what can I do to avoid such discrepancies in the future?
Your change event did not fire because you had an extra false as the last parameter of your change event binding. I am not sure why.
Also make sure you do not have any other script errors in the page.
This should work fine.
$(document).ready(function () {
$("#Account_AccountName").on("change", function () {
alert($(this).val());
var jsonData = { account: $(this).val() };
// your get JSON call goes here
alert("Success");
});
});
Also it might be a good idea to define the getDmUrl variable before using it. I recommend using javascript namespacing instead of simply using a global variable.
<script>
var myProj = myProj || {};
myProj.getDmUrl = '#Url.Action("GetDMList", "Project", new { area = "Masters" })';
</script>
<script src="~/Scripts/DMS/projectMasterNew.js"></script>
And in your projectMasterNew.js, use myProj.getDmUrl for your ajax call.
Related
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 currently having difficulty using Ajax to update a partial view without having to refresh the whole page. I'm using MVC and entity framework to scaffold views.
I'll try and include as much as possible to help explain myself:
I have a div which is going to be used to hold a list view of all my comments
<div id="ContainAnnouncementComments"> </div>
This div gets populated using the following:
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Custom_Scripts/BuildAnnouncement.js"></script>
#Scripts.Render("~/bundles/jqueryval")
$(document).ready(function () {
$.ajax({
url: '/Comments/BuildAnnouncementComment',
data: { AnnouncementId: #Model.AnnouncementId},
success: function (result) {
$('#ContainAnnouncementComments').html(result);
}
});
});
Which calls the BuildAnnouncementComment() method in my controller:
public ActionResult BuildAnnouncementComment(int? AnnouncementId)
{
return PartialView("_AnnouncementComment", db.Comments.ToList().Where(x => x.AnnouncementId == AnnouncementId));
}
I then have a second div container which is used to hold a text box for a user to enter some information which 'should' then update the ContainAnnouncementComments using Ajax replace call:
<div id="ContainAnnouncementCommentCreate">
#using (Ajax.BeginForm("AJAXCreate", "Comments", new { announcementId = Model.AnnouncementId }, new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "ContainAnnouncementComments"
}))
{
<div class="form-group">
#Html.AntiForgeryToken()
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
}
</div>
The Ajax method calls the AJAXCreate method in the controller:
public ActionResult AJAXCreate(int announcementId, [Bind(Include = "CommentId, Message")] Comment comment)
{
if (ModelState.IsValid)
{
comment.UserId = User.Identity.GetUserId();
comment.AnnouncementId = announcementId;
db.Comments.Add(comment);
db.SaveChanges();
}
return PartialView("_AnnouncementComment", db.Comments.ToList());
}
From here, when running, I try to create a new comment, but when submitted, instead of the partialView being updated, all that is being displayed is the partialview.
Not sure if I've explained this properly so if i'm missing any information please let me know and i'll update accordingly.
After 3 hours of staring at my code I realised that nothing was actually wrong with the implementation and all that was wrong was I hadn't installed AJAX through the NuGet manager.
Joys.
I have a cshtml viewpage like below
<div class="form-group">
#foreach (var fields in Model.ListProductFields)
{
#Html.LabelFor(x => x.ProductFieldNameEn, fields.FieldName, htmlAttributes: new { id="", #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.FieldValue, new { htmlAttributes = new { #class = "form-control", #row = 5 } })
</div>
}
</div>
using above div section I can populate multiple textareas with its relavant label
this is how it shows when this is working
Then I try to add summernote rich text editor for all of these text areas ,
using following home-index.js file
(function ($) {
function HomeIndex() {
var $this = this;
function initialize() {
$('#FieldValue').summernote({
focus: true,
height: 150,
codemirror: {
theme: 'united'
}
});
}
$this.init = function () {
initialize();
}
}
$(function () {
var self = new HomeIndex();
self.init();
})
}(jQuery))
then its apply for first text area. not apply for rest of the text areas
like below
Your creating invalid html because each textarea has the same id attribute and ('#FieldValue') will only ever return the first element with id="FieldValue".
In addition this will not bind to your model when you submit the form. Instead, use a for loop to generate the html and give the textarea a class name for use as a jQuery selector (note that property ListProductField will need to implement IList or alternatively you can use a custom EditorTemplate for the type)
#for (int i = 0; i < Model.ListProductFields.Count; i++)
{
#Html.LabelFor(x => x.ListProductFields[i].ProductFieldNameEn, Model.ListProductFields[i].FieldName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(m => m.ListProductFields[i].FieldValue, new { htmlAttributes = new { #class = "form-control summernote", #row = 5 } })
</div>
}
and change the selector to
$('.summernote').summernote({
Side note: It appears your #Html.LabelFor() is incorrect. It is creating a label associated with property ProductFieldNameEn but you control is for property FieldName. I think you have the parameters the wrong wa around and it should be
#Html.LabelFor(x => x.ListProductFields[i].FieldName, Model.ListProductFields[i].ProductFieldNameEn, ...
which will associated the label with the following textarea, and display the value of property ProductFieldNameEn
<div class="form-group">
#foreach (var fields in Model.ListProductFields)
{
#Html.LabelFor(x => x.ProductFieldNameEn, fields.FieldName, htmlAttributes: new { id="", #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.FieldValue, new { htmlAttributes = new { #class = "form-control mySummerNote", #row = 5 } })
</div>
}
</div>
(function ($) {
function HomeIndex() {
var $this = this;
function initialize() {
$('.mySummerNote').summernote({
focus: true,
height: 150,
codemirror: {
theme: 'united'
}
});
}
$this.init = function () {
initialize();
}
}
$(function () {
var self = new HomeIndex();
self.init();
})
}(jQuery))
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.
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.