Cascading population of a mvc dropdown - javascript

I have 2 DropDowns in an MVC application. Trying to populate the second one based on the first one's chosen value (Cascading)..
Since I am rather new to MVC this is giving me a little problem..
This is how ot looks in my View..
I am getting the data based on the selection in the first DropDown
This is the JS code for retriving the data..
<script type="text/javascript">
$('#AllShips').change(function () {
var selectedShip = $("#AllShips").val();
console.log(selectedShip);
var arrivalSelect = $('#AllShipArrivals');
arrivalSelect.empty();
if (selectedShip != null && selectedShip != '') {
$.getJSON('#Url.Action("GetArrivals")', { ShipID: selectedShip }, function (arrivals) {
console.log(arrivals);
if (arrivals != null && !jQuery.isEmptyObject(arrivals))
{
arrivalSelect.append($('<option/>', {
value: null,
text: ""
}));
$.each(arrivals, function (index, arrival) {
console.log(arrival.Value);
console.log(arrival.Text);
arrivalSelect.append($('<option/>', {
value: arrival.Value,
text: arrival.Text
}));
});
};
});
}
});
Here is my HTML
<div class="col-lg-2 form-group">
#Html.LabelFor(model => model.AllShips, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#if (Model.AllShips != null)
{
#Html.DropDownListFor(model => model.ShipID, Model.AllShips, new { #class = "form-control", id = "AllShips" })
#Html.ValidationMessageFor(model => model.ShipID, "", new { #class = "text-danger" })
}
</div>
</div>
<div class="col-lg-2 form-group">
#Html.LabelFor(model => model.AllShipArrivals, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#if (Model.AllShipArrivals != null)
{
#Html.DropDownListFor(model => model.ArrivalId, Model.AllShipArrivals, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ArrivalId, "", new { #class = "text-danger" })
}
else
{ #Html.DropDownListFor(model => model.ArrivalId, null, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ArrivalId, "", new { #class = "text-danger" })}
</div>
</div>
Still the second DropDown is not changing (initially I populate it with a list of all Arrivals, yes I know..not a good option)
Any idea what I am doing wrong here??

Thanks for the HTML, it is what I suspected. In your JS your are referencing 2nd DDL by var arrivalSelect = $('#AllShipArrivals'); but there is no such element (it is, but this is just a label), instead you should use:
var arrivalSelect = $('#ArrivalId');

Make 'PartialView' or 'ViewComponent' for the second Dropdown, do ajax call on changing selection in the first dropdown, choose data depending on the value you passed during the ajax call, pass the data into your partial view and return it.
Something like
public IActionResult SecondDropdownPartial(int selectedShip)
{
var data = GetSomeData(selectedShip);
return Partial("ViewName", data);
}
In js 'success' you get your view in response. Replace it on the page with
$('#container').html(response);
for example.
More information : Rendering Partial Views using ajax

edited
try making the value to be empty string rather than null
emptyStr="";
arrivalSelect.append('<option value="${emptyStr}"> ${emptyStr} </option>');
arrivalSelect.append('<option value="${arrival.Value}"> ${arrival.Text} </option>');

Related

Custom Client Side Validation through JavaScript

I am developing a project in MVC 5. There is are some form input field where I need to put custom client side validation using jquery/javascript. The expected behaviour is for example when someone tries to type alphabets or special characters in the phone input, there should be a validation error displayed below the field or atleast an alert box should get triggered containing the error. I have added the custom validation file in my scripts folder. I can see some basic logs I wrote on the console of the page. I am facing challenges on the js function where we can capture the id of the field and provide custom logic for it. Here is my code. Please suggest what can be done.
#model StudentMVCApp.Models.registration
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm(new
{
#id = "registerFormId",
#class = "form-horizontal",
role = "form"
}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Register a new student</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.firstname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.firstname, new { htmlAttributes = new { #class = "form-control", data_val = "true", data_val_required = "Please dont provide empty name!" } })
#Html.ValidationMessageFor(model => model.firstname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.lastname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.lastname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.lastname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.phone, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.phone, new { htmlAttributes = new { #class = "form-control",#id="phoneid" } })
#Html.ValidationMessageFor(model => model.phone, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.email, "", 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 {
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/Unobtrusive")
#Scripts.Render("~/CustomValidation")
}
Here is my Custom JavaScript
console.log("I am working JS");
(function ($) {
console.log("This function is captured");
var phone = $("#phoneid").val();
console.log(phone);
if (phone != null)
console.log(phone);
else if (phone == 100)
alert($(this).val());
else if (phone == "abc")
alert(phone);
else if (phone == abc)
alert(phone)
})(jQuery);
I tried different tutorials and experimented with some js functions. But couldn't get it working using id of the field.
I would recommend using jquery.validation.unobstrusive package scripts on your view:
<script src="~/Scripts/Jquery/jquery-1.9.1.js"></script>
<script src="~/Scripts/Jquery/jquery.validate.js"></script>
<script src="~/Scripts/Jquery/jquery.validate.unobtrusive.js"></script>
and adding your own custom validation on a form element by writing jquery such as:
$("#myinput").rules("add", {
required: true,
minlength: 2,
messages: {
required: "Required input",
minlength: jQuery.validator.format("Field needs to be more than{0}")
}
});
More info can be found at https://jqueryvalidation.org/rules/
You need to attach your code above to an event, input or submit for example.
Now it only runs once, when the page loads
something like
$(function() {
console.log("This function is captured");
const $phone = $('#phoneid');
$phone.on('input',function() {
const val = $(this).val(); // get the value - it is a string
console.log(val);
if (val == 100) console.log('100'); // note the == coerces the numeric string to number
else if (phone == 'abc') console.log('abc');
});
});

Get data from textbox to pass to controller without refreshing page/submitting form in MVC

Right now, on my page I have a button at the top of one of my views <button type="button" id="show">Find Project</button> that when pressed, renders a partial view (which is a table) at the bottom of the page. My layout is formatted as:
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<!--this is my navbar-->
</div>
<div class="container body-content">
#RenderBody()
</div>
<div class="wrapper">
<!-- TABLE WILL APPEAR HERE -->
</div>
</body>
The javascript that makes the table appear is:
$(document).ready(function() {
$("#show").on("click", function() {
$.ajax({
url: '/Projects/SearchTable',
type: "GET"
}).done(function(partialViewResult) {
$(".wrapper").html(partialViewResult);
$(".wrapper").css('display', 'block');
});
});
});
/Projects/SearchTable is the controller action that renders the partial view:
[HttpGet]
public ActionResult SearchTable()
{
var states = GetAllStates();
var model = new ProjectClearanceApp.Models.ProjectViewModel();
model.States = GetSelectListItems(states);
model.Projects = from m in db.Projects select m;
return PartialView("~/Views/Projects/_ClearedProjects.cshtml", model);
}
The text boxes are dispersed throughout my main view, and all appear in the same format:
<div class="col-md-5 entry-field">
#Html.LabelFor(model => model.Project.FirstNamedInsured, htmlAttributes: new { #class = "control-label" })
#Html.TextBoxFor(model => model.Project.FirstNamedInsured, new { #class = "form-control text-box single-line", maxlength=150 })
#Html.ValidationMessageFor(model => model.Project.FirstNamedInsured, "", new { #class = "text-danger" })
</div>
Except for two dropdowns that are formatted like so:
<div class="col-md-3 address-info">
#Html.LabelFor(m => m.Project.FirstNamedInsuredAddress.State, htmlAttributes: new { #class = "address-label" })
#Html.DropDownListFor(m => m.Project.FirstNamedInsuredAddress.State,
Model.States,
"--",
new { #class = "form-control address-entry" })
#Html.ValidationMessageFor(model => model.Project.FirstNamedInsuredAddress.State, "", new { #class = "text-danger" })
</div>
where Model.States is an enumerable to populate the drop down list with all of the states.
Is it possible to get the data from those textboxes and pass them as data/arguments to the controller for use in the partial view without submitting a form or refreshing the page?
Send the parameter like this:
public ActionResult SearchTable(string firstNamedInsured)
{
//Use the parameter in the model or any where
var states = GetAllStates();
var model = new ProjectClearanceApp.Models.ProjectViewModel();
model.States = GetSelectListItems(states);
model.Projects = from m in db.Projects select m;
return PartialView("~/Views/Projects/_ClearedProjects.cshtml", model);
}
And javascript:
$(document).ready(function () {
$("#show").on("click", function () {
$.ajax({
url: '/Projects/SearchTable?firstNamedInsured=' + $('#' + #Html.IdFor(m => m.Project.FirstNamedInsured)).val(),
type: "GET"
}).done(function (partialViewResult) {
$(".wrapper").html(partialViewResult);
$(".wrapper").css('display', 'block');
});
});
});

How to pass data from a form to my controller, and then back to my client-side javascript?

I have a form in my webpage that asks for first and last name. When the user submits this form I would like the data to be passed to a function in my controller, where it is used to find the corresponding user in our Active Directory. I want to then return the matching Email address to a Javascript function, when then displays that information on my page. Is this the "correct" way to go about doing this, and if yes, how would I structure a function in my controller to accept form input, and return data to my client side javascript?
What I have so far in my controller:
public SearchResult[] searchAD(String fname, String lname)
{
Func<System.DirectoryServices.ActiveDirectory.Domain> domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain;
System.DirectoryServices.DirectoryEntry root = new DirectoryEntry("LDAP://"+domain);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchRoot = root;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenName={0})(sn={1}))", fname, lname);
SearchResult[] results = new SearchResult['a'];
searcher.FindAll().CopyTo(results, 0);
return results;
}
And my form:
#using (Html.BeginForm("searchAD", "AD", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.firstName, "First Name", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.firstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.firstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.lastName, "Last Name", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.lastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.lastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div>
<input type="submit" value="Search" class="btn" />
</div>
</div>
}
You can Store value in TempData and get it on view
public ActionResult searchAD(String fname, String lname)
{
Func<System.DirectoryServices.ActiveDirectory.Domain> domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain;
System.DirectoryServices.DirectoryEntry root = new DirectoryEntry("LDAP://"+domain);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchRoot = root;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenName={0})(sn={1}))", fname, lname);
SearchResult[] results = new SearchResult['a'];
searcher.FindAll().CopyTo(results, 0);
TempData["Result"]=results;
return View("ViewName");
}
In View (Jquery)
$(function(){
if ('#TempData["Result"]' != '')
{
// call your function here and pass TempData Value
}
});
Above is not perfect solution for your posted issue because tempdata or viewbag are not meant for that.If you need to post data again back from control to view then its always make use of #Ajax.BeginForm
#using (Ajax.BeginForm("ActionName", "Controller", new AjaxOptions { OnComplete = "Sucess" }))
in jquery you have to write Success function like below :
function Sucess(arg) {
//arg.data
}
in arg.data you will get that object what you are passing from controller.More ever #Html.BeginForm refreshing your whole page not specific content but with With Ajax.begin form you can manage to reload only those content which is under ajax.begin form.

MVC - jQuery validation does not work on dynamically added elements

I know this kind of issue has been solved many times already, however I am unable to get mine fixed based on solutions provided.
I am building a simple library application. There is a feature to add a copy of a book, which uses jQuery to invoke controller actions and return partial views which are then added dynamically to the DOM.
The last dynamically element added is a form with additional details of a created copy. The ajax call is being triggered when a value of a DropDownList (#AuthorBooksDropDown) (also added dynamically) changes.
$('#authorBooksPlaceHolder').on('change', '#AuthorBooksDropDown', function () {
var bookId = $(this).val();
$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {
$('#bookDetailsPlaceHolder').html(data);
$('#bookDetailsPlaceHolder').slideDown();
});
$.validator.unobtrusive.parse('#addCopyForm');
});
The call invoked the AddCopy_RenderDetails action get an entity from a DB based on book id, and creates a new copy with certain fields populated.
Controller action:
public PartialViewResult AddCopy_RenderDetails(int id)
{
var book = db.LibraryBooks.Find(id);
var newCopy = new Book()
{
Author = book.Author,
Title = book.Title,
Publisher = book.Publisher,
CollectionId = book.CollectionId,
Collection = book.Collection
};
return PartialView("_AddCopy_Details", newCopy);
}
The view displays remaining fields which need to be populated.
#model CityLibrary.Models.Library.Book
<div class="vertical-separator"></div>
<hr />
#using (Ajax.BeginForm("AddCopy", "Books", new AjaxOptions
{
UpdateTargetId = "bookDetailsPlaceHolder"
}, new { #id = "addCopyForm" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Author)
#Html.HiddenFor(model => model.Title)
#Html.HiddenFor(model => model.CollectionId)
#Html.HiddenFor(model => model.Collection.Name)
#Html.HiddenFor(model => model.Publisher)
<div class="form-group">
#Html.LabelFor(model => model.Collection.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Collection.Name, new { htmlAttributes = new { #class = "form-control", #disabled = "" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ISBN, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ISBN, new { htmlAttributes = new { #class = "form-control", } })
#Html.ValidationMessageFor(model => model.ISBN, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Publisher, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Publisher, new { htmlAttributes = new { #class = "form-control", #disabled = "disabled" } })
#Html.ValidationMessageFor(model => model.Publisher, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.YearPrinted, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.YearPrinted, new { htmlAttributes = new { #class = "form-control", #Value = "" } })
#Html.ValidationMessageFor(model => model.YearPrinted, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-success btn-block" />
</div>
</div>
}
Even though I have $.validator.unobtrusive.parse('#addCopyForm'); invoked when the form is rendered (checked in chrome dev tools), validation still happens on the server side upon pressing a submit button as a POST action is being triggered every time. Not to mention that validation errors do not display upon TABing to next field.
Validation attributes are there in form's inputs:
I also have remote validation which checks whether a entered ISBN is already in the database. Obviously this works on the client side, which in my case simply does not.
Thank you for your time and help.
EDIT:
Well, I've added the following to the end of the view:
<script>
$.validator.unobtrusive.parse('#addCopyForm');
</script>
And it works. I have no idea why triggering it on a function does nothing.
Ajax is async, and your $.validator.unobtrusive.parse('#addCopyForm'); line of code is being called before the html has been added to the DOM. Move it to inside the success callback
$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {
$('#bookDetailsPlaceHolder').html(data);
$('#bookDetailsPlaceHolder').slideDown();
$.validator.unobtrusive.parse('#addCopyForm');
});
Try this peace of code
$("form").on("submit", function (e) {
e.preventDefault();
$.validator.unobtrusive.parse($('#addCopyForm')); // here you need define your form id
if ($(this).valid()) // use to validate the form
{
//do ajax call
$.ajax({
type: "Post",
url: "/Books/AddCopy_RenderDetails/" + bookId,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
}
});
}
});

ASP.NET MVC jQuery JSON result redirecting URL

Using MVC/Json/Jquery.
Using form to create a new "group".
Form is on ~Group/Manage, posting form to ~Group/Create
Whilst working on this, returning Json result was working fine, handling in Jquery, no URL redirection.
Now, everytime I run it, it redirects me to ~Group/Create and displays the Json result.
Controller Group/Create
[HttpPost]
public ActionResult Create([Bind(Include="name,description")] GroupModel groupmodel)
{
...
return Json(new { success = true, message = groupmodel.name }, JsonRequestBehavior.AllowGet);
}
Form
<form id="frm_createGroup" action="/Groups/Create" method="post">
<h2>Create Group</h2>
<div class="form-group">
#Html.LabelFor(model => model.name, new { #for = "name" })
#Html.TextBoxFor(model => model.name, new { #class = "form-control", #placeholder = "Group Name" })
#Html.ValidationMessageFor(model => model.name)
</div>
<div class="form-group">
#Html.LabelFor(model => model.description, new { #for = "description" })
#Html.TextBoxFor(model => model.description, new { #class = "form-control", #placeholder = "Group Description" })
#Html.ValidationMessageFor(model => model.description)
</div>
<span id="createGroupMessage"></span>
<button type="submit" class="btn btn-primary pull-right">Create</button>
</form>
Jquery to handle form
$(document).ready(function (){
$('#navGroups').makeActiveMenuItem();
var options = {
success: groupCreateSubmitted
,error: groupCreateError
}
$('#frm_createGroup').ajaxForm(options);
});
function groupCreateSubmitted(responseText, statusText, xhr, $form) {
if (responseText.success)
{
$('#createGroupMessage').html = "Group Created";
}
else
{
$('#createGroupMessage').html = responseText.message;
}
}
To be clear, I don't want URL redirection, I just want the Jquery to catch the return (it was before, have no idea why its changed...)
Thanks!
removed
,error: groupCreateError
working now...form bind was failing.

Categories