knockout check all checkboxes in table column - foreach

I am new to knockout and trying to implement a table with the first column as a checkbox. When I click the header the whole column should get checked / unchecked. At the moment, it works when I click table rows (all rows get checked/unchecked) but not on table header. Please let me know what is wrong with my code! Here is parts of my code :
<table>
<tr>
<th><input type="checkbox" data-bind="click: selectAll"></th>
<th>Notes</th>
</tr>
<tbody data-bind="foreach: DocumentRows">
<tr>
<td><input type="checkbox" data-bind="checked: $parent.IsSelected"></td>
<td><data-bind="text: Notes"></td>
</tr>
</tbody>
</table>
// Script.ts
```
define(['knockout', 'jquery', 'text!./Template'], (ko, $, htmlString) => {
//Document
class Document {
Id: KnockoutObservable<number>;
Notes: KnockoutObservable<string>;
constructor(data?) {
this.Id = ko.observable(0);
this.Notes = ko.observable("").extend({ defaultValue: "" });
if (data != null) {
ko.mapping.fromJS(data, {}, this);
}
}
};
//DocumentS VIEW MODEL
class DocumentsViewModel {
DocumentRows: KnockoutObservableArray<Document>;
IsSelected: KnockoutObservable<boolean>;
constructor(params) {
this.DocumentRows = ko.observableArray([]);
this.IsSelected = ko.observable(false);//
this.InitComputed();
this.Load();
}
InitComputed = () => {
selectAll = (Document: DocumentsViewModel) => {
var doc = Document.DocumentRows;
ko.utils.arrayForEach(doc(), function (item) {
item.IsSelected(true);
});
}
Load = () => {
DocumentsApiService.GetDocumentList(this);
}
}
//API SERVICE
class DocumentsApiService {
static GetDocumentList = (model: DocumentsViewModel) => {
$.ajax({
url: buildUrl(model.LoadListURL, { 'id': model.ObjectId(), 'additionalId': model.AdditionalId() }),
type: 'POST',
data: ko.mapping.toJSON(model.Filter),
contentType: 'application/json; charset=utf-8'
}).done(allData => {
var mapped = ko.mapping.fromJS(allData, DocumentsMapping);
model.DocumentRows(mapped.DocumentRows());
model.Filter.TotalCount(mapped.TotalCount());
model.Filter.PageIndex(mapped.Filter.PageIndex());
CalcCountTableStatus(model.Filter.PageIndex(), model.Filter.PageSize(), model.Filter.TotalCount(), 'documents-count-status-line');
}).fail(data => {
TSCore.OnFailure(data);
});
}
}
return { viewModel: DocumentsViewModel, template: htmlString }
});
```

When implementing such a selection feature, usually there are two approaches.
Have an isSelected boolean observable at the Document level
Put the isSelected observable on the Document model and no on the DocumentsViewModel, because each document must hold the information whether it's selected or not.
I guess you were trying to implement this approach, but at first glance, you do not have the IsSelected at the right place. Also, if you move IsSelected to the document, then in you view do not use $parent.IsSelected, but just IsSelected.
Maintain a selectedDocuments observable array of documents at the DocumentsViewModel level
This way you can have an observable array of all the currently selected documents, and maintain that when selecting all of them (like push all the rows to the selection)
In your view, you must use a parent reference to determine whether the document is selected similar to for example $parent.selectedDocuments.indexOf($data) >= 0.
I'll not cover all the pros and cons of the two approach for now, but I suggest you use the first one, if you can extend your document model with an IsSelected observable.
Other notes
It's not quite clear what you want to achieve with the InitComputeds stuff. Why don't you just have these actions as functions on your viewmodel class like
public selectAll(): void {
ko.utils.arrayForEach(this.DocumentRows(), function (item) {
item.IsSelected(true);
});
}
If you use this syntax, you must bind the function at the view level like $data.selectAll.bind($data). If you do not want that, and you want just purely selectAll, then you can use arrow function as a field syntax like this.
public selectAll = ():void => {
ko.utils.arrayForEach(this.DocumentRows(), function (item) {
item.IsSelected(true);
});
}
Hope I could give some valuable hints.

So, after changes
Table header:
<th><input type="checkbox" data-bind="click: selectAll, checked: selectedAll"></th>
Table body:
<td><input type="checkbox" data-bind="checked: IsSelected"></td>
Script:
//Document
class Document {
Id: KnockoutObservable<number>;
IsSelected: KnockoutObservable<boolean>;
constructor(data?) {
this.Id = ko.observable(0);
this.Notes = ko.observable("").extend({ defaultValue: "" });
this.IsSelected = ko.observable(false);
if (data != null) {
ko.mapping.fromJS(data, {}, this);
}
}
};
//DocumentS VIEW MODEL
class DocumentsViewModel {
selectedAll: KnockoutObservable<boolean>;
constructor(params) {
this.selectedAll = ko.observable(false);
}
InitComputed = () => {
this.selectedAll = ko.pureComputed({
read: function () {
return this.selectedIds().length === this.DocumentRows().length;
},
write: function (value) {
this.selectedIds(value ? this.DocumentRows.slice(0) : []);
},
owner: this
}
selectAll = (): void => {
if (this.selectedIds().length > 0) {
this.selectedIds.removeAll();
ko.utils.arrayForEach(this.DocumentRows(), function (item) {
item.IsSelected(false);
});
} else {
ko.utils.arrayPushAll(this.selectedIds(), this.DocumentRows())
ko.utils.arrayForEach(this.DocumentRows(), function (item) {
item.IsSelected(true);
});
}
}
}
It doesn't show the header to be checked when all rows are checked or unchecked when all are unchecked. Any pointers where I am going wrong please?

Related

C# Core MVC - <script> populate a textarea after dropdown list onchange event

I'm trying to fill a text area on my MVC view after retrieving a subset of data. I can get the data, but need to update the view with the returned data
Here is the code I have tried:
<div class="box-shadow">
<p>Please select an Area Path and Iteration Path below:</p>
<table>
<tr>
<th>Area Path</th>
<th>Iteration Path</th>
</tr>
<tr>
<td>
#Html.DropDownList("MySpecialAreaPathsList",((List<string>)ViewBag.myAreaPathsList).Select(m => new SelectListItem { Text = m, Value = m }),
null, new { #onchange = "GetAreaPathValue()" })
</td>
<tr>
<td>
<textarea class="text-info" id="txtLog" style="width:1080px;height:200px"></textarea>
</td>
function GetAreaPathValue() {
var selectedElement = document.querySelector('#MySpecialAreaPathsList');
var option = selectedElement.value;
$.ajax({
url: '#Url.Action("GetAreaPathChildren", "MyController")',
type: "GET",
data: { 'areapathText': option },
success: function (data) { $('#txtLog').value(data) }
})
}
Can someone help me with getting the return data from GetAreaPathChildren (the return value is a List but I'd be happy just getting a string (or any value actually)
I'm trying to fill a text area on my MVC view after retrieving a
subset of data. I can get the data, but need to update the view with
the returned data
Well, based on your shared code snippet, I have successfully reproduced your scenario. The reason why your data is not appending to your textarea as expected has pretty obvious reason. If you check your browser console.log you would get below error:
Reason Of Error:
You are using wrong javascript attribute value. Its incorrect. It should be val insteaed of value. Therefore, your code snippet would be
$('#txtLog').val(data.message) instead of
$('#txtLog').value(data)
Complete Solution
Controller:
public class AppendTextAreaController : Controller
{
public IActionResult Index()
{
List<string> MySpecialAreaPathsList = new List<string>();
MySpecialAreaPathsList.Add("C#");
MySpecialAreaPathsList.Add("SQL");
MySpecialAreaPathsList.Add("Asp.net core");
ViewBag.myAreaPathsList = MySpecialAreaPathsList;
return View();
}
[HttpGet]
public JsonResult GetAreaPathChildren(string areapathText)
{
return new JsonResult(new { message = string.Format("Data From Controller and parameter passed: {0}",areapathText) });
}
}
View:
<div class="box-shadow">
<p>Please select an Area Path and Iteration Path below:</p>
<table>
<tr>
<th>Area Path</th>
<th>Iteration Path</th>
</tr>
<tr>
<td>
#Html.DropDownList("MySpecialAreaPathsList",((List<string>)ViewBag.myAreaPathsList).Select(m => new SelectListItem { Text = m, Value = m }),
null, new { #onchange = "GetAreaPathValue()" })
</td>
<tr>
<td>
<textarea class="text-info" id="txtLog" style="width:1080px;height:200px"></textarea>
</td>
</table>
</div>
#section Scripts {
<script>
function GetAreaPathValue() {
alert("Inside Func");
var selectedElement = document.querySelector('#MySpecialAreaPathsList');
var option = selectedElement.value;
$.ajax({
url: '#Url.Action("GetAreaPathChildren", "AppendTextArea")',
type: "GET",
data: { 'areapathText': option },
success: function (data) {
console.log(data);
$('#txtLog').val(data.message)
}
})
}
</script>
}
Output:
Note:
Remember that, if you return your data from controller like this
var message = string.Format("Data From Controller and parameter passed: {0}", areapathText);
return new JsonResult(message);
In that scenario, you should get this data in your view as below:
$('#txtLog').val(data)
You could refer below code snippet as well.
[HttpGet]
public JsonResult GetAreaPathChildren(string areapathText)
{
var message = string.Format("Data From Controller and parameter passed: {0}", areapathText);
return new JsonResult(message);
}

Get and Set Value in drop downdown in MVC4

I am a beginer ...I don't know how to use dropdown in MVC this....I have used it like this
In ItemMaster.cshtml
#Html.DropDownList("ProductName", ViewData["ProductName"] as SelectList)
In Controller.cs
public ActionResult ItemMaster(Item model)
{
ObservableCollection<Item> ItemList = new ObservableCollection<Item>();
Item Item = new Models.Item();
ItemList = Item.GetItemList();
Item Product = new Item();
DataTable dtProduct = new DataTable();
dtProduct = Item.GetProductList();
IList<Item> MyList = new List<Item>();
foreach (DataRow mydataRow in dtProduct.Rows)
{
MyList.Add(new Item()
{
Id = Convert.ToInt32(mydataRow["Id"].ToString().Trim()),
Product_Name = mydataRow["Name"].ToString().Trim()
});
}
var ProductName = new SelectList(MyList, "Id", "Product_Name");
ViewData["ProductName"] = ProductName;
return View(ItemList);
}
I am using Item list to fill grid view.... And I am using view data to fill drop down list....It is working fine.... I don't know how to get selected value when Button is clicked.
Try this,
#Html.DropDownList("ProductName", ViewData["ProductName"] as SelectList)
<input type="button" id="btnasd" value="Click"/>
Script
<script type="text/javascript">
$(document).ready(function () {
$("#btnasd").click(function () {
var Id = $("#ProductName").val();
$.ajax({
url: '#Url.Action("Action", "Controller")',
type: "Post",
data: { ProductNameId: Id },
success: function (result) {
$("#mygrid").html('');
$("#mygrid").append(result.Data);
}
});
});
});
</script>
Do following(for onchange event of DropDownList):
#Html.DropDownList("ProductName", ViewData["ProductName"] as SelectList,
"-Select Product-", new { onchange = "doFunction();" })
javascript:
<script type="text/javascript">
$(document).ready(function () {
doFunction();
});
function doFunction() {
var PassVal = $("#ProductName").val(); //It has dropdownlist's selected value.
if (PassVal != '') {
//Do Ajax operations to load data in GridView(On Same Page).
$.ajax({
url: '<CONTROLLER/ACTIONANME>', //Specify Actionname in controller from which you will get data.
type: "POST",
data: {
ProductName: PassVal
},
dataType: "html",
success: function (data) {
$("#GridView").empty(data); //empty gridview
$("#GridView").html(data); //Load data to gridview
},
error: function () {
alert("No Records Found");
}
});
}
}
</script>
Or On button click
#Html.DropDownList("ProductName", ViewData["ProductName"] as SelectList,
"-Select Product-")
<input type="button" id="btnSubmit" value="Submit"/>
script:
$('#btnSubmit').click(function(){
var PassVal = $("#ProductName").val(); //It has dropdownlist's selected value.
if (PassVal != '') {
//Do Ajax operations to load data in GridView(On Same Page).
$.ajax({
url: '<CONTROLLER/ACTIONANME>', //Specify Actionname in controller from which you will get data.
type: "POST",
data: {
ProductName: PassVal
},
dataType: "html",
success: function (data) {
$("#GridView").empty(data); //empty gridview
$("#GridView").html(data); //Load data to gridview
},
error: function () {
alert("No Records Found");
}
});
}
});
Ask me if you have any query.
Note: You can also use DropDownListFor for model binded dropdown.

Using jQuery's Ajax to do instantaneous calculation on the view in mvc2

I have got a total product page which shows the total amount of the products added to the basket , What I want to do is to add a promo text field where users can add the promo code to get certain discounts , the promo code are save in the database with discount rate. So if a client enters the code , and press the promo button the system should check for the valid promo code and then do instaneous calculation by deducting the total price with the discount rate . Is this can be done using jQuery Ajax or is there any other possible solution for it , Any help or tutorial will be highly appreciated. Cheers
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#promo").change(function () {
var $this = $(this);
var number = $('promoNumber').val();
$.ajax({
url: '/Booking/Review',
type: "POST",
data: {number:textbox},
success: function (data) {
if (data["success"]) {
alert(data)
}
}
});
});
});
</script>
<%using (Html.BeginForm())
{ %>
<table>
<tr>
<td> <input type="button" id="promo" value="Promo" /> </td>
<td> <%: Html.TextBox("promoNumber") </td>
</tr>
<tr><td>
<div class="totalCost">
<label><b>Total Amount:</b></label> <%: String.Format("{0:c}", ViewBag.TotalAmount)%> <br /></div>
</td></tr>
</table> }%>
Controller
[HttpPost]
public ActionResult Review(int number)//this parameter is my selected checkbox value.I have to get this value from ajax
{
IVoucherRepository voucherResp = new VoucherRepository();
IQueryable<Voucher> getVoucher = voucherResp.GetVouchers(number);
//first check if the code is there or not else return error
int discount = 0;
foreach (var number in getVoucher)
{
discount = number.discount;//Thats the discount number
}
//after getting the discount code i need to pass that to ViewBag.TotalAmount to do calculation
ViewData["Data"] = "success";
return View();
}
I've seen a problem when you're posting your data to the controller:
var number = $('promoNumber').val();
and then:
data: {number:textbox},
What is textbox?
You should refer to your promoNumber like this in jQuery:
var number = $('#promoNumber').val();
This is what I would do:
$(document).ready(function() {
$("#promo").click(function() {
$.ajax({
type: 'POST',
url: 'Booking/Review',
dataType: 'json',
data: { number: $("#promoNumber").val() },
success: function(data) {
if (data) {
alert(data.discount);
alert(data.totalAmount);
}
}
});
});
});
Since you're using a button I would use click event.
I am using jSon as a return object.
and this is the controller:
[HttpPost]
public ActionResult Review(int number)
{
IVoucherRepository voucherResp = new VoucherRepository();
IQueryable<Voucher> getVoucher = voucherResp.GetVouchers(number);
//first check if the code is there or not else return error
int discount = 0;
int totalAmount = 0;
foreach (var number in getVoucher)
{
discount = number.discount;//Thats the discount number
}
//after getting the discount code i need to pass that to ViewBag.TotalAmount to do calculation
return (Json(new { discount = discount, totalAmount = totalAmount }, JsonRequestBehavior.DenyGet));
}
in your controller you can do all the calculations and then return a json object with all the properties you want.
Attach a jQuery post event to the coupon button:
$('input[name="coupon"]').click(function() {
$.ajax({
type: 'POST',
url: myUrl,
data: {code: $('input[name="code"]').val()},
success: function(discount) {
/* logic here. may need to store the code and it's discount
if more than one code is allowed */
}
});
});
Like ghayes mentioned though, if there are existing solutions, use those. They will, most likely, have considered many situations unknown to you.

Cascading drop-downs in MVC 3 Razor view

I am interested in how to implement cascading dropdown lists for addresses in a Razor view. My Site entity has a SuburbId property. Suburb has a CityId, and City has ProvinceId. I would like to display dropdowns for all of Suburb, City, and Province on the Site view, where e.g. the suburb dropdown will initially display "First select a City", and the City dropdown, "First select a province". On selecting a province, cities in the province are populated etc.
How can I achieve this? Where do I start?
Let's illustrate with an example. As always start with a model:
public class MyViewModel
{
public string SelectedProvinceId { get; set; }
public string SelectedCityId { get; set; }
public string SelectedSuburbId { get; set; }
public IEnumerable<Province> Provinces { get; set; }
}
public class Province
{
public string Id { get; set; }
public string Name { get; set; }
}
Next a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fetch those from your repository
Provinces = Enumerable.Range(1, 10).Select(x => new Province
{
Id = (x + 1).ToString(),
Name = "Province " + x
})
};
return View(model);
}
public ActionResult Suburbs(int cityId)
{
// TODO: Fetch the suburbs from your repository based on the cityId
var suburbs = Enumerable.Range(1, 5).Select(x => new
{
Id = x,
Name = "suburb " + x
});
return Json(suburbs, JsonRequestBehavior.AllowGet);
}
public ActionResult Cities(int provinceId)
{
// TODO: Fetch the cities from your repository based on the provinceId
var cities = Enumerable.Range(1, 5).Select(x => new
{
Id = x,
Name = "city " + x
});
return Json(cities, JsonRequestBehavior.AllowGet);
}
}
And finally a view:
#model SomeNs.Models.MyViewModel
#{
ViewBag.Title = "Home Page";
}
<script type="text/javascript" src="/scripts/jquery-1.4.4.js"></script>
<script type="text/javascript">
$(function () {
$('#SelectedProvinceId').change(function () {
var selectedProvinceId = $(this).val();
$.getJSON('#Url.Action("Cities")', { provinceId: selectedProvinceId }, function (cities) {
var citiesSelect = $('#SelectedCityId');
citiesSelect.empty();
$.each(cities, function (index, city) {
citiesSelect.append(
$('<option/>')
.attr('value', city.Id)
.text(city.Name)
);
});
});
});
$('#SelectedCityId').change(function () {
var selectedCityId = $(this).val();
$.getJSON('#Url.Action("Suburbs")', { cityId: selectedCityId }, function (suburbs) {
var suburbsSelect = $('#SelectedSuburbId');
suburbsSelect.empty();
$.each(suburbs, function (index, suburb) {
suburbsSelect.append(
$('<option/>')
.attr('value', suburb.Id)
.text(suburb.Name)
);
});
});
});
});
</script>
<div>
Province:
#Html.DropDownListFor(x => x.SelectedProvinceId, new SelectList(Model.Provinces, "Id", "Name"))
</div>
<div>
City:
#Html.DropDownListFor(x => x.SelectedCityId, Enumerable.Empty<SelectListItem>())
</div>
<div>
Suburb:
#Html.DropDownListFor(x => x.SelectedSuburbId, Enumerable.Empty<SelectListItem>())
</div>
As an improvement the javascript code could be shortened by writing a jquery plugin to avoid duplicating some parts.
UPDATE:
And talking about a plugin you could have something among the lines:
(function ($) {
$.fn.cascade = function (options) {
var defaults = { };
var opts = $.extend(defaults, options);
return this.each(function () {
$(this).change(function () {
var selectedValue = $(this).val();
var params = { };
params[opts.paramName] = selectedValue;
$.getJSON(opts.url, params, function (items) {
opts.childSelect.empty();
$.each(items, function (index, item) {
opts.childSelect.append(
$('<option/>')
.attr('value', item.Id)
.text(item.Name)
);
});
});
});
});
};
})(jQuery);
And then simply wire it up:
$(function () {
$('#SelectedProvinceId').cascade({
url: '#Url.Action("Cities")',
paramName: 'provinceId',
childSelect: $('#SelectedCityId')
});
$('#SelectedCityId').cascade({
url: '#Url.Action("Suburbs")',
paramName: 'cityId',
childSelect: $('#SelectedSuburbId')
});
});
Thanks Darin for your lead to the solution. It greatly helped me to arrive to the point. But as 'xxviktor' mentioned, I did got circular ref. error. To get rid of it, I've done this way.
public string GetCounties(int countryID)
{
List<County> objCounties = new List<County>();
var objResp = _mastRepo.GetCounties(countryID, ref objCounties);
var objRetC = from c in objCounties
select new SelectListItem
{
Text = c.Name,
Value = c.ID.ToString()
};
return new JavaScriptSerializer().Serialize(objRetC);
}
And to achieve auto cascading, I've slightly extended jQuery extension this way.
$('#ddlCountry').cascade({
url: '#Url.Action("GetCounties")',
paramName: 'countryID',
childSelect: $('#ddlState'),
childCascade: true
});
And the actual JS is using this parameter as below (inside JSON request).
// trigger child change
if (opts.childCascade) {
opts.childSelect.change();
}
Hope this helps someone with similar issue.
be aware, that this solution doesn't work directly with EF 4.0. It causes "A circular reference was detected while serializing..." error. Here are possible solutions http://blogs.telerik.com/atanaskorchev/posts/10-01-25/resolving_circular_references_when_binding_the_mvc_grid.aspx , I've used second one.
To implement cascading drop down lists that support MVC's built in validation and binding, you will need to do something a little different than what is done in the other answers here.
If your model has validation, this will support it. An excerpt from a model with validation:
[Required]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public Guid cityId { get; set; }
In your controller you need to add a get method, so that your view will be able to get the relevant data later:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetData(Guid id)
{
var cityList = (from s in db.City where s.stateId == id select new { cityId = s.cityId, name = s.name });
//simply grabbing all of the cities that are in the selected state
return Json(cityList.ToList(), JsonRequestBehavior.AllowGet);
}
Now, to the View that I mentioned earlier:
In your view you have two drop downs similar to this:
<div class="editor-label">
#Html.LabelFor(model => model.stateId, "State")
</div>
<div class="editor-field">
#Html.DropDownList("stateId", String.Empty)
#Html.ValidationMessageFor(model => model.stateId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.cityId, "City")
</div>
<div class="editor-field">
#*<select id="cityId"></select>*#
#Html.DropDownList("cityId", String.Empty)
#Html.ValidationMessageFor(model => model.cityId)
</div>
The content in the drop downs are bound by the controller, and are automatically populated. Note: in my experience removing this binding and relying on java script to populate the drop downs make you lose validation. Besides, the way we are binding here plays nice with validation, so there is no reason to change it.
Now onto our jQuery plugin:
(function ($) {
$.fn.cascade = function (secondaryDropDown, actionUrl, stringValueToCompare) {
primaryDropDown = this; //This doesn't necessarily need to be global
globalOptions = new Array(); //This doesn't necessarily need to be global
for (var i = 0; i < secondaryDropDown.options.length; i++) {
globalOptions.push(secondaryDropDown.options[i]);
}
$(primaryDropDown).change(function () {
if ($(primaryDropDown).val() != "") {
$(secondaryDropDown).prop('disabled', false); //Enable the second dropdown if we have an acceptable value
$.ajax({
url: actionUrl,
type: 'GET',
cache: false,
data: { id: $(primaryDropDown).val() },
success: function (result) {
$(secondaryDropDown).empty() //Empty the dropdown so we can re-populate it
var dynamicData = new Array();
for (count = 0; count < result.length; count++) {
dynamicData.push(result[count][stringValueToCompare]);
}
//allow the empty option so the second dropdown will not look odd when empty
dynamicData.push(globalOptions[0].value);
for (var i = 0; i < dynamicData.length; i++) {
for (var j = 0; j < globalOptions.length; j++) {
if (dynamicData[i] == globalOptions[j].value) {
$(secondaryDropDown).append(globalOptions[j]);
break;
}
}
}
},
dataType: 'json',
error: function () { console.log("Error retrieving cascading dropdown data from " + actionUrl); }
});
}
else {
$(secondaryDropDown).prop('disabled', true);
}
secondaryDropDown.selectedindex = 0; //this prevents a previous selection from sticking
});
$(primaryDropDown).change();
};
} (jQuery));
You can copy the above jQuery that i created, into <script>...</script> tags in your view, or in a separate script file if you wish (note I updated this to make it cross browser, however the scenario in which i was using is no longer required, it should work however).
In those same script tags, (not in a separate file) you can call the plugin by using the following javascript:
$(document).ready(function () {
var primaryDropDown = document.getElementById('stateId');
var secondaryDropdown = document.getElementById('cityId');
var actionUrl = '#Url.Action("GetData")'
$(primaryDropDown).cascade(secondaryDropdown, actionUrl);
});
Remember to add the $(document).ready part, the page must be fully loaded before you try to make the drop downs cascade.
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
//Dropdownlist Selectedchange event
$("#country").change(function () {
$("#State").empty();
$.ajax({
type: 'POST',
url: '#Url.Action("State")', // we are calling json method
dataType: 'json',
data: { id: $("#country").val() },
// here we are get value of selected country and passing same value
success: function (states) {
// states contains the JSON formatted list
// of states passed from the controller
$.each(states, function (i, state) {
$("#State").append('<option value="' + state.Value + '">' +
state.Text + '</option>');
// here we are adding option for States
});
},
error: function (ex) {
alert('Failed to retrieve states.' + ex);
}
});
return false;
})
});
</script>
<div>
#Html.DropDownList("country", ViewBag.country as List<SelectListItem>, "CountryName", new { style = "width: 200px;" })
</div>
<div>
</div>
<div>
#Html.DropDownList("State", ViewBag.country as List<SelectListItem>)
</div>
From controller I am getting the values
public async Task<ActionResult> Country()
{
Country co = new Country();
List<SelectListItem> li = new List<SelectListItem>();
li.Add(new SelectListItem { Text = "Select", Value = "0" });
li.Add(new SelectListItem { Text = "India", Value = "1" });
li.Add(new SelectListItem { Text = "Nepal", Value = "2" });
li.Add(new SelectListItem { Text = "USA", Value = "3" });
li.Add(new SelectListItem { Text = "Kenya", Value = "4" }); ;
ViewBag.country= li;
return View();
}
public JsonResult state(string id)
{
List<SelectListItem> states = new List<SelectListItem>();
states.Add(new SelectListItem { Text = "--Select State--", Value = "0" });
switch (id)
{
case "1":
states.Add(new SelectListItem { Text = "MP", Value = "1" });
states.Add(new SelectListItem { Text = "UP", Value = "2" });
break;
case "3":
states.Add(new SelectListItem { Text = "USA1", Value = "3" });
states.Add(new SelectListItem { Text = "USA2", Value = "4" });
break;
}
return Json(new SelectList(states, "Value", "Text", JsonRequestBehavior.AllowGet));
}

How to update strongly typed Html.DropDownList using Jquery

I have a webpage with two radiobuttons and a dropdownlist as follows:
<div class="sectionheader">Course
<div class="dropdown"><%=Html.DropDownList("CourseSelection", Model.CourseList, new { #class = "dropdown" })%> </div>
<div class="radiobuttons"><label><%=Html.RadioButton("CourseType", "Advanced", false )%> Advanced </label></div>
<div class="radiobuttons"><label><%=Html.RadioButton("CourseType", "Beginner", true )%> Beginner </label></div>
</div>
The dropdownlist is strongly typed and populated with Model.CourseList (NB - on the first page load, 'Beginner' is the default selection and the dropdown shows the beginner course options accordingly)
What I want to be able to do is to update the DropDownList based on which radiobutton is selected i.e. if 'Advanced' selected then show one list of course options in dropdown, and if 'Beginner' selected then show another list of courses.
Edit - posted my own answer below to show solution that worked for me (finally!)
Continue to return your collection of selectlistitem; this translates to JSOn nicely, at least it should, as an array of objects that look like { text: "a", value: "1" } and you can loop through the array and recreate the list this way...
So it will work with strongly-typed objects. You just have to take the objects and construct the elements for the underlying drop down.
HTH.
The code I would like to call sits within my Controller:
public ActionResult UpdateDropDown(string courseType)
{
IDropDownList dropdownlistRepository = new DropDownListRepository();
IEnumerable<SelectListItem> courseList = dropdownlistRepository.GetCourseList(courseType);
return Json(courseList);
}
Using examples provided in jQuery in Action, I now have the following jQuery code:
$('.radiobuttons input:radio').click(function()
{
var courseType = $(this).val(); //Get selected courseType from radiobutton
var dropdownList = $("#CourseSelection"); //Ref for dropdownlist
$.post("/ByCourse/UpdateDropDown", { courseType: courseType }, function(data) {
$(dropdownList).loadSelect(data);
});
});
The loadSelect function is taken straight from the book and is as follows:
(function($) {
$.fn.emptySelect = function() {
return this.each(function() {
if (this.tagName == 'SELECT') this.options.length = 0;
});
}
$.fn.loadSelect = function(optionsDataArray) {
return this.emptySelect().each(function() {
if (this.tagName == 'SELECT') {
var selectElement = this;
$.each(optionsDataArray, function(index, optionData) {
var option = new Option(optionData.Text, optionData.Value);
if ($.browser.msie) {
selectElement.add(option);
}
else {
selectElement.add(option, null);
}
});
}
});
}
})(jQuery);

Resources