I am creating a list of partial views on my main view. The partial view uses cascade dropdown lists. The cascade dropdowns work well on the first row however when I add a second row and change the value on the first dropdown the 2 second ones also get change instead of he one in the row I am just updating.
I researched and know that it is because of my script is using the same tag for both of them and not being unique.
I started using Html.BeginCollectionItem()
to get unique names on my dropdowns and modified my script to get the value of the hidden field and tie to the right dropdown but it is not working.
Any ideas how can I have unique ids in my script?
This is my partial view:
@model BudgetPortalMVC4.Models.NewBudgetModel
@using (Html.BeginCollectionItem(""))
{
<script type="text/jscript">
$(function () {
var value = document.getElementsByName(".index")[0].value;
$(document).on('change', "#["+value+"].SelectedCategory", function () {
$("#["+value+"].SelectedSubCategory").empty();
$.getJSON('@Url.Action("GetSubCategories")', { id: $(this).val() }, function (data) {
if (!data) {
return;
}
$("#["+value+"].SelectedSubCategory").append($('<option></option>').val('0').text('Select Item'));
$.each(data, function (index, item) {
$("#["+value+"].SelectedSubCategory").append($('<option></option>').val(item.Value).text(item.Text));
})
});
});
});
</script>
<table>
<tr>
<td>
<div>
@Html.LabelFor(m => m.SelectedCategory)
@Html.DropDownListFor(m => m.SelectedCategory, Model.CategoriesList, "Please select", new { @class = "SelectedCategory" })
@Html.ValidationMessageFor(m => m.SelectedCategory)
</div>
</td>
<td>
<div>
@Html.LabelFor(m => m.SelectedSubCategory)
@Html.DropDownListFor(m => m.SelectedSubCategory, Model.SubCategoriesList, "Please select", new { @class = "SelectedSubCategory" })
@Html.ValidationMessageFor(m => m.SelectedSubCategory)
</div>
</td>
</tr>
</table>
}
This is part of the code it generates when rendered:
<input type="hidden" name=".index" autocomplete="off" value="b6fadec7-eb7d-4ce2-a06c-79ef0597b8da" />
<table>
<tr>
<td>
<div>
<label for="">SelectedCategory</label>
<select class="SelectedCategory" data-val="true" data-val-number="The field SelectedCategory must be a number." name=[b6fadec7-eb7d-4ce2-a06c-79ef0597b8da].SelectedCategory"><option value="">Please select</option>
<option value="1">Groceries</option>
<option value="2">Travel</option>
</select>
<span class="field-validation-valid" data-valmsg-for="[b6fadec7-eb7d-4ce2-a06c-79ef0597b8da].SelectedCategory" data-valmsg replace="true"></span>
An this is the error my script is throwing when trying to make the tag unique:
Error: Syntax error, unrecognized expression: #[4eebf546-988d-44a0-aa4e-296c46273d54].SelectedCategory throw new Error( "Syntax error, unrecognized expression: " + msg );
Scripts should never be in partials. Move the script to the main view (or layout) and use your elements class names in jQuery
selectors.
You partial should be
@using (Html.BeginCollectionItem(""))
{
<div class="item">
<label>
<span>@Html.DisplayNameFor(m => m.SelectedCategory)</span>
@Html.DropDownListFor(m => m.SelectedCategory, Model.CategoriesList, "Please select", new { @class = "SelectedCategory" })
@Html.ValidationMessageFor(m => m.SelectedCategory)
</label>
<label>
<span>@Html.DisplayNameFor(m => m.SelectedSubCategory)</span>
@Html.DropDownListFor(m => m.SelectedSubCategory, Model.SubCategoriesList, "Please select", new { @class = "SelectedSubCategory" })
@Html.ValidationMessageFor(m => m.SelectedSubCategory)
</label>
</div>
}
Note that because your model is IEnumerable<T>
, the HtmlHelper
methods will not generate an id
attribute oro your form controls, so if you want labels, they need to wrap the control.
Note also that even if id
attributes were created (assumingyour model was an object containing a property for the collection), the HtmlHelper
methods replace any [
, ]
and .
characters with and underscore to prevent a conflict with jQuery
selectors, so something like #[4eebf546-988d-44a0-aa4e-296c46273d54].SelectedCategory
would never exist in the view.
Then change the script to
var url = '@Url.Action("GetSubCategories", "Budget")'; // Do not hard code your url's
$(document).on('change', '.SelectedCategory', function() {
var subCategory = $(this).closest('.item').find('.SelectedSubCategory');
subCategory.empty();
$.getJSON(url, { id: $(this).val() }, function (data) {
if (!data) {
return;
}
subCategory.append($('<option></option>').val('').text('Please select')); // Do not give the option a value!
$.each(data, function (index, item) {
subCategory.append($('<option></option>').val(item.Value).text(item.Text));
});
});
});
Note you also replace $(document)
with the closest ancestor that exists in the document when the page is first rendered (e.g. that might be $('form')
)