I have an MVC 4 app with a View containing two dropdown lists. The user selects a value in the first dropdown and then an Ajax call is made to populate the second dropdown based on the contents of the first.
My JavaScript code looks as follows and gets called when the user selects an item in the first dropdown:
function GetAutoModel(_manufacturerId) {
var autoSellerListingId = document.getElementById("AutoSellerListingId").value;
$.ajax({
url: "/AutoSellerListing/GetAutoModel/",
data: { manufacturerId: _manufacturerId, autoSellerListingId: autoSellerListingId },
cache: false,
type: "POST",
success: function (data) {
var markup = "<option value='0'>-- Select --</option>";
for (var x = 0; x < data.length; x++) {
**if (data[x].Selected) {**
markup += "<option selected='selected' value=" + data[x].Value + ">" + data[x].Text + "</option>";
}
else
markup += "<option value=" + data[x].Value + ">" + data[x].Text + "</option>";
}
$('#autoModel').html(markup).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
The Ajax call works correctly. However, the data that gets returned for the second dropdown contains a selected item and I'm trying to detect the selected item (via the 'if' statement), and render the HTML appropriately. The problem is that 'Selected' doesn't seem to be a property of 'data' because each value evaluates to false, even though one of the values is true.
Am I doing something wrong? Or is there a better way to do this?
The following is the controller code:
[HttpPost]
public ActionResult GetAutoModel(int manufacturerId, int autoSellerListingId)
{
int modelId = 0;
// Get all the models associated with the target manufacturer
List<AutoModel> modelList = this._AutoLogic.GetModelListByManufacturer(manufacturerId);
// If this is an existing listing, get the auto model Id value the seller selected.
if (autoSellerListingId > 0)
modelId = this._systemLogic.GetItem<AutoSellerListing>(row => row.AutoSellerListingId == autoSellerListingId).AutoModel.AutoModelId;
// Convert all the model data to a SelectList object
SelectList returnList = new SelectList(modelList, "AutoModelId", "Description");
// Now find the selected model in the list and set it to selected.
foreach (var item in returnList)
{
if (item.Value == modelId.ToString())
item.Selected = true;
}
return Json(returnList);
}
Try this instead (add modelId to constructor of SelectList, and remove the foreach block):
// Convert all the model data to a SelectList object
SelectList returnList = new SelectList(modelList, "AutoModelId", "Description", modelId);
return Json(returnList);
Related
I am fetching the data using API and assign it to VIEW BAG but at run time it shows me an error.
List<DevInfo> DevList = await RestApi.Instance.GetAllDevAsync();
var nameT = DevList.Select(a=>a.Name).ToList();
ViewBag.datasourceDevList = nameT;
And in the script.
var nameList = <%= new JavaScriptSerializer().Serialize("#ViewBag.datasourceDevList") %>;
I want to generate the dropdown list.
if (nameList != '') {
var tableHtml;
$.each(JSON.parse(nameList), function (index, value) {
//tableHtml += "<option value=" + value.Name + ">" + value.Name + "</option>";
console.log(value);
});
/*$("#selectTrainer").html(tableHtml);*/
$("#selectTrainer").append(tableHtml);
}
Instead of calling JavaScriptSerializer() in javascript which makes the code looks complex, you can achieve generating the options via these ways:
Solution 1: Using razor syntax
view
<select id="selectTrainer">
#if (ViewBag.datasourceDevList != null)
{
foreach(var option in ViewBag.datasourceDevList)
{
<option value="#option.Name">#option.Name</option>
}
}
</select>
Sample Solution 1
Solution 2: Pass IEnumerable<SelectListItem> to #Html.DropDownListFor
Set your ViewBag with IEnumerable<SelectListItem> element. #Html.DropDownListFor will generate the options based on list items.
controller
using System.Linq;
ViewBag.datasourceDevList = DevList
.Select(x => new SelectListItem { Text = x.Name, Value = x.Name } )
.ToList();
view
#Html.DropDownListFor(m => m.DevID,
(IEnumerable<SelectListItem>)ViewBag.datasourceDevList,
htmlAttributes: new
{
#id = "selectTrainer"
})
Sample Solution 2
References
DropDownList(HtmlHelper, String, IEnumerable)
I have a listbox in view.
This Listbox use template
Listbox
<div id="UsersLoad" style="width: 50%">
#Html.EditorFor(i => i.Users, "UsersForEdit")
</div>
Template UserForEdit (Part of the code)
#model string[]
#{
if (this.Model != null && this.Model.Length > 0)
{
foreach(var item in this.Model)
{
listValues.Add(new SelectListItem { Selected = true, Value = item, Text = item });
}
}
else
{
listValues = new List<SelectListItem>();
}
}
<div class="field-#size #css">
<h3>#Html.LabelFor(model => model):</h3>
#Html.ListBoxFor(model => model, listValues, new { id = id })
</div>
In another view div "Users" is called.
function LoadUsersCheckBox() {
$("#UsersLoad").load('#Url.Action("LoadUsers", "User")' + '?idUser=' + idUser);
}
LoadUsers Controller
public JsonResult LoadUsers(int? idUser)
{
var users = Service.GetSystemUsers(idUser);
var model = users.Select(x => new
{
Value = x,
Description = x
});
return this.Json(model, JsonRequestBehavior.AllowGet);
}
The controller method returns what I want.
But instead of it select the items in the listbox it overwrites the listbox with only the text of found users.
How to mark the items in the listbox on the function LoadUsersCheckBox?
Sorry for my bad English
The jQuery load() method "loads data from the server and places the returned HTML into the matched element." Note the words "the returned HTML". See http://api.jquery.com/load/
To select existing items, you should try get() instead (http://api.jquery.com/jQuery.get/). In the success callback handler, you will need to parse the returned data to an array. Then use an iterator to go over the items in the listbox, and if they exist in the parsed array, mark them as selected. Something like:
$.get("action url", function(data) {
var users = $.parseJSON(data);
$("#UsersLoad option").each(function() {
var opt = $(this),
value = opt.attr("value");
opt.removeAttr("selected");
if (users.indexOf(value) > -1) {
opt.attr("selected", "selected");
}
});
});
transaction.executeSql('SELECT * FROM Table1 ORDER BY date DESC', [],
function(transaction, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
$('#records').append('<li date = "'+row['date']+'" data-rowid2="' + row['Id'] + '">' + 'test: ' + row['column1'] + '</li>').trigger('change');
}
}
},errorHandler);
transaction.executeSql('SELECT * FROM Table2 ORDER BY date DESC', [],
function(transaction, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
$('#records').append('<li date = "'+row['date']+'" data-rowid="' + row['Id'] + '">'+ 'Test2: ' + row['column1'] +'</li>').trigger('change');
}
$("#records").listview().listview("refresh");
}
},errorHandler);
},errorHandler,nullHandler);
This is some code which selects columns from 2 different tables. The tables have two different columns called date which stores the date input. Notice the date attribute assigned to the li elements. My problem comes here:
$(document).on("pageinit", "#mypage", function(){
$("#records").listview({
autodividers: true,
autodividersSelector: function (li) {
var out = li.attr("date");
return out;
}
}).listview("refresh");
});
Now since the dates are from two different tables, two autodividers are being created even if they share the same date. For example if table1 date column had 2014-01-02 and table2 date column had 2014-01-02, two separate dividers would be created for the same date and the list elements wouldn't be shown under one. Is there a solution to this problem?
NOTE: Both the select statements append results to the same listview, they are just from different tables
EDIT: I think I have found what the issue is, but I am not sure on how to solve it. Basically, when I append those list elements, Table1 stuff takes priority and always goes first. For example if I had the list like this:
Table 1 item
Table 2 item
Then if I added another Table 1 item it will do this:
Table 1 item
Table 1 item
Table 2 item
Which is displacing the table 2 item from its original position. Is there anyway to solve this?
I use bootstrap multi-select and I want to update options on flow with ajax
To populate on init my multiselect I do
<select name="model" class="multiselect" multiple="multiple">
<? foreach ($sel_models as $mod) { ?>
<option value="<?= $mod ?>" <?= ($mod == $params['model']) ? 'selected' : '' ?>><?= $mod ?></option>
<? } ?>
</select>
then on event I would like to update my option list with the following ajax
I was trying to use the rebuild method but won't fire the drop-down after creation
$.ajax({
type: 'post',
url: "helper/ajax_search.php",
data: {models: decodeURIComponent(brands)},
dataType: 'json',
success: function(data) {
$('select.multiselect').empty();
$('select.multiselect').append(
$('<option></option>')
.text('alle')
.val('alle')
);
$.each(data, function(index, html) {
$('select.multiselect').append(
$('<option></option>')
.text(html.name)
.val(html.name)
);
});
$('.multiselect').multiselect('rebuild')
},
error: function(error) {
console.log("Error:");
console.log(error);
}
});
With firebug I can see that the list is generated but on select won't show up
In the doc I can read :
.multiselect('setOptions', options)
Used to change configuration after initializing the multiselect. This may be useful in combination with .multiselect('rebuild').
Maybe you can't change your widget data by your initial way. In a correct way you should use setOptions method.
Else : With your way, maybe should you think about destroy your widget .multiselect('destroy') and create it again after.
Update after comment :
In the doc : ( you've linked )
Provides data for building the select's options the following way:
var data = [
{label: "ACNP", value: "ACNP"},
{label: "test", value: "test"}
];
$("#multiselect").multiselect('dataprovider', data);
So :
When you get data from your ajax call, you have to create an array of objects ( it's the options in the select you want to have ) with the format like
var data =
[
{label: 'option1Label', value: 'option1Value'},
{label: 'option2Label', value: 'option2Value'},
...
]
When your objects array is created, then you just have to call the method
$("#multiselect").multiselect('dataprovider', data);
Where data is your array of objects.
I hope I'm clear :/
As an alternative to multiselect('dataprovider', data) you can build the list with jquery append exactly the way you did in your question. The only change you need to make is to delay the rebuild until after the ajax request is complete.
var buildDrivers = $.getJSON('resources/orders/drivers.json', function(data) {
$.each(data, function(i, driver) {
$('#toolbar select[name="drivers"]').append('<option>'+driver+'</option>');
});
});
buildDrivers.complete(function() {
$('.multiselect').multiselect('rebuild');
});
see http://api.jquery.com/jquery.getjson/ for documentation
I've been added the functionality of updating options after filtering and getting them from the server side. This solution relays on the concept of injecting new options, destroying the select and initializing it again.
I took into account:
Considering the existing selected options, which must stay.
Removing duplicate options (might be as a conflict from which that already selected and the new that came from the server).
Keeping the options tray open after the update.
Reassign the previous text in the search text box & focusing it.
Just add the 'updateOptions' as a function after the 'refresh' function along with the two helper functions as follows:
updateOptions: function (options) {
var select = this.$select;
options += this.getSelectedOptionsString();
var selectedIds = select.val(),
btnGroup = select.next('.btn-group'),
searchInput = btnGroup.find('.multiselect-search'),
inputVal = searchInput.val();
options = this.removeOptionsDuplications(options);
if (!options) {
options = '<option disabled></option>';
}
// 1) Replacing the options with new & already selected options
select.html(options);
// 2) Destroyng the select
select.multiselect('destroy');
// 3) Reselecting the previously selected values
if (selectedIds) {
select.val(selectedIds);
}
// 4) Initialize the select again after destroying it
select.multiselect(this.options);
btnGroup = select.next('.btn-group');
searchInput = btnGroup.find('.multiselect-search');
// 5) Keep the tray options open
btnGroup.addClass('open');
// 6) Setting the search input again & focusing it
searchInput.val(inputVal);
searchInput.focus();
},
getSelectedOptionsString: function () { // Helper
var result = '',
select = this.$select,
options = select.find('option:selected');
if (options && options.length) {
$.each(options, function (index, value) {
if (value) {
result += value.outerHTML;
}
});
}
return result;
},
removeOptionsDuplications: function (options) { // Helper
var result = '',
ids = new Object();
if (options && options.length) {
options = $(options);
$.each(options, function (index, value) {
var option = $(value),
optionId = option.attr('value');
if (optionId) {
if (!ids[optionId]) {
result += option[0].outerHTML;
ids[optionId] = true;
}
}
});
}
return result;
},
Demo:
State:
"Option 1"
$('#select').multiselect('updateOptions', '<option value="2">Option 2</option>');
State:
"Option 2"
"Option 1"
I think this is an easier way to add options on the fly (using ajax or any other listener) to an existing Bootstrap MultiSelect.
Following is a simplified example to add options:
function addOptionToMultiSelect(multiselectSelector, value, selected) {
var data = [];
$(multiselectSelector + ' option').each(function(){
var value = $(this)[0].value;
var selected = $(this)[0].selected;
data.push({label: value, value: value, selected: selected});
});
// Add the new item
data.push({label: value, value: value, selected: selected});
$(multiselectSelector).multiselect('dataprovider', data);
}
For simplicity, I have assumed both the label and value are the same in an option. Note that the already selected options are taken care of by reading the selected attribute from the existing options. You can make it more sophisticated by tracking the disabled and other attributes.
Sample:
addOptionToMultiSelect('#multiselect-example', 'new-option', true);
I've been using this tutorial to create a screen where a user can add additional input fields on a given screen
Instead of using all textboxes Iv'e created a UIHint/Partial view that'll render a multi-select JQuery widget (I'm using this component)
ViewModel for each row item
public class Micros
{
[UIHint("JsonComboBox")]
[AdditionalMetadata("id", "Lab_T_ID")]
[AdditionalMetadata("description", "Description")]
[AdditionalMetadata("action", "LabTestOptions")]
[AdditionalMetadata("controller", "Micro")]
[AdditionalMetadata("noneSelectedText", "Test")]
[AdditionalMetadata("comboboxWidth", "200")]
[DisplayName("Test")]
public Nullable<int> Lab_T_ID { get; set; }
[UIHint("JsonComboBox")]
[AdditionalMetadata("id", "Lab_SD_ID")]
[AdditionalMetadata("description", "Description")]
[AdditionalMetadata("action", "LabSampleDetailOptions")]
[AdditionalMetadata("controller", "Micro")]
[AdditionalMetadata("noneSelectedText", "Sample Details")]
[AdditionalMetadata("comboboxWidth", "300")]
[DisplayName("Sample Details")]
public Nullable<int> Lab_SD_ID { get; set; }
[DisplayName("Result")]
public string Result { get; set; }
}
Partial View/UIHint
#model int?
#{
var values = ViewData.ModelMetadata.AdditionalValues;
}
<select id="#values["id"]" multiple="multiple" style="width:#values["comboboxWidth"]px" > </select>
<script type="text/javascript">
$.getJSON('#Url.Action((string)values["action"], (string)values["controller"])',
null,
function (j) {
var options = '';
for (var i = 0; i < j.length; i++) {
options += '<option value="' + j[i].#values["id"] + '">' + j[i].#values["description"] + '</option>';
}
$('##values["id"]').html(options);
$('##values["id"] option:first').attr('selected', 'selected');
});
setTimeout(function () {
$("##values["id"]").multiselect({
multiple: false,
header: "Select an option",
noneSelectedText: '#values["noneSelectedText"]',
selectedList: 1
});
}, 300);
</script>
The components render fine on the initial page load, but when add the items, they get added... but it seems that the javascript doesn't execute/get added..
Any ideas? Still debugging this issue, will post the fix as soon as I find it, but I'm hoping someone can point me in the right direction
Update
So far I've discovered that (We'll it looks like), the UIHint/Partials don't get rendered at all when the user clicks to add another item. (Otherwise the select will be populated with items, and the JQuery widget will be applied)
I would recommend you to remove all javascript from your partial. Javascript shouldn't be mixed with markup. So your editor template should contain only markup:
#model int?
#{
var values = ViewData.ModelMetadata.AdditionalValues;
}
<span>
<select multiple="multiple" style="width:#values["comboboxWidth"]px" data-url="#Url.Action((string)values["action"], (string)values["controller"])" data-noneselectedtext="#values["noneSelectedText"]" data-value="#values["id"]" data-text="#values["description"]"></select>
</span>
and then in a separate javascript file you will have a function which will be used when the Add another... button is clicked as shown in Steven Sanderson's article:
$('#addItem').click(function() {
$.ajax({
url: this.href,
cache: false,
success: function(html) {
// Here's the place to attach any plugins to the given row:
var select = $('select', html);
$.getJSON(select.data('url'), function(options) {
$.each(options, function() {
select.append(
$('<option/>', {
value: this[select.data('value')],
text: this[select.data('text')]
})
);
});
select.multiselect({
multiple: false,
header: 'Select an option',
noneSelectedText: select.data('noneselectedtext'),
selectedList: 1
});
$('#editorRows').append(select);
});
}
});
return false;
});