Below is my first piece of knockoutjs code i've written. In the code below, i have two list boxes. the first is populated after the page loads and works fine. What i want to do next is that when a user selects an item from the cmbDataSets listbox, i want to make a second ajax call to populate the 'instruments' property of my view model. later when the user selects an instrument, i want to make yet another call to fetch data that i will display in a grid (using slickgrid.js).
Right now, i'd like to understand what are the ways or best practice for accomplish this. i think i can simply add normal html/javascript selection change event handler on the first list box to accomplish this...but i'm not sure if that is the recommended way (i know it's not the MVVM way anyway). I feel that since selectedDataSet is an observable, i should be able to chain that to an event handler as well..no? My question is how? Can i define an onSelectedDataSetChange method on my viewmodel and if so, how do i 'hook' it into the actually selection change of the cmbDataSets control?
<div class="container">
<label for="cmbDataSets">Select list:</label>
<select id="cmbDataSets" data-bind="options: dataSets, optionsText:'descr', value:selectedDataSet, optionsCaption:'Choose' " class="form-control"></select>
<label for="cmbInstruments">Select instrument:</label>
<select id="cmbInstruments" data-bind="options: instruments, optionsText:'intrument', value:selectedInstrument, optionsCaption:'Choose' " class="form-control"></select>
</div>
<script type="text/javascript">
$(document).ready(function () {
var viewModel = {
dataSets: ko.observableArray(),
instruments: ko.observableArray(),
selectedDataSet: ko.observable(),
selectedInstrument: ko.observable()
}
$.ajax({
url: '/ds/sets',
type: "GET",
dataType: "json",
success: function (data) {
debugger;
console.log(data);
viewModel.dataSets(data);
}
});
ko.applyBindings(viewModel);
});
</script>
You can subscribe to the first boxes selectedOption observable and make a call whenever it changes.
selectedOption = ko.observable();
selectedOption.subscribe(function (newValue) {
secondBoxSource(ajaxCallFunction(newValue));
});
Where ajaxCallFunction() is the function you use to fetch the data for the second box, and newValue is the newly selected value from the first box.
Mike.
Check this code, for setting change event in the View:
<select data-bind="event: { change: selectionChanged }"></select>
and then a proerty in the ViewModel:
selectionChanged: function(event) { }
Is this is what you were searching for? Other than this, I have a small suggestions - SelectedDataSet and selectedInstrument can be also observable arrays. The difference is that you are going to use not the 'value' biding, but the 'selectedOptions' one. This will help you when you have multiple selection, but even when it's a single one, it's a better option, I think.
Related
function GenericSelectFirst(e){
// This function is called without any errors, I have verified it
e.sender.select(0);
}
<select class="full-width"
name="..."
data-bind="source: ..., value: ..., visible: ..., events:{dataBound: Function.GenericSelectFirst}"
data-role="dropdownlist"
data-value-field="..."
data-text-field="..."
data-value-primitive="true"
data-auto-bind="true"
required="required"
>
</select>
I did not think that it is that hard to achieve such a common easy scenario, but I have a Kendo DropDownList which initialize using MVVM style, and it is binding a remote datasource.
What I want to achieve is that, once the remote datasource is ready and bound to the widget, the first option is selected by default (and of course the value of the first item should be bound to the view model)
I tried to do it directly with the above code, which binds an dataBound event to the widget, and select the first item when it fires. The callback method has been called without errors, but the widget never select the first option but keep selecting the first default "empty" option.
What did I do wrong and how can I fix it? Any advice is appreciated!
Wich Kendo version are you using? If latest, remember that since
V. Q1 2015 , In order to match the Html Select behavior better and solve some issues related to MVVM value binding, the dropdownlist now allows to clear its value (deselect the selected item). This will introduce the following breaking changes:
The widget will not select the first item, when its selected index is -1
The widget will not select the first item, when the selected value is not present in the data source.
Long story short, If someone wants to get the previous behavior, then he/she will need to select first item manually:
dataBound: function() {
if (this.select() === -1) { //check whether any item is selected
this.select(0);
this.trigger("change");
}
}
I am trying to achieve a databinding for multiple selection with optgroup using knockoutJS. In addition we would like to use select2 for its search and display capabilities.
Here is the fiddle sample.
Everything works well when the items are added directly using the html control. You may pickup some countries in the example above and click the view button to see that the code of the countries are well retrieved. However, I would like to populate the items another way. Precisely, I created a command to flush the observable array containing the selected items and force the first item in the list of available options to be selected (which is the country Laos in our example). This command is executed when clicking the second button.
After clicking this latter button, you can check that the observable selectedCountries contains the expected values by clicking the first button. Unfortunately, the UI control is not refreshed, do you have an idea how to do that? The html databiding for my view looks like
<select class="multi-select" data-bind="foreach: availableCountries,selectedOptions:selectedCountries" multiple="multiple">
<optgroup data-bind="attr: {label: label}, foreach: children">
<option data-bind="text: display, value: code"></option>
</optgroup>
</select>
The short answer is that Select2 doesn't know about changes you make to the underlying model.
I was able to make your sample work using a hack, see here: http://jsfiddle.net/bXPM6/
The changes made are:
<select id="foo" class="multi-select" data-bind="foreach: availableCountries, selectedOptions:selectedCountries" multiple="multiple">
(Note the added id=foo).
And I added a subscription to the observable:
function MyViewModel(){
var self = this;
self.availableCountries = ko.observableArray(app.availableCountries());
self.selectedCountries = ko.observableArray([]);
// added this bit
self.selectedCountries.subscribe(function (newValue) {
$('#foo').select2("val", newValue);
});
}
The better option is to make a custom knockout binding that can keep Select2 updated with changes to your model.
Something like this:
HTML:
<select class="multi-select" data-bind="foreach: availableCountries, selectedOptions:selectedCountries, select2: selectedCountries" multiple="multiple">
JavaScript:
ko.bindingHandlers.select2 = {
update: function (element, valueAccessor) {
$(element).select2("val", ko.unwrap(valueAccessor()) || "");
}
};
Hope this is of some help.
I finally got bindings for the options and value to work on my dropdown component. What I have now works except that all javascript after the applyBinding for the value is not executed. This is not a problem when I only have one dropdown on the page at a time.
However, most of the time, I need to use more than one. When I step through the script on such a page, it executes the applyBinding for the first dropdown and stops. No further script runs, and only the first dropdown works.
Why is this? More importantly, how do I fix it?
Here are the relevant lines of code:
$(function () {
var $thisdd = $("##ddname"); //the JQuery selector for my dropdown
var dropdownItems = getDropdownItemsFromDl("#ddname");
var newitem = ko.observable({ cname: ddcname, cvalue: ko.observable($thisdd.val()), cpublishtopic: "" });
classificationsViewModel.push(newitem());
var viewModel =
{
dditems : dropdownItems
};
$("##ddname").attr("data-bind", "value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue, options: dditems, optionsText: 'value', optionsValue: 'key'");
ko.applyBindings(viewModel);
ko.applyBindings(classificationsViewModel()[classificationsViewModel.indexOf((newitem())].cvalue);
});
When you call ko.applyBindings, Knockout will apply the model to the entire document.
This is great if you have one model for the entire page.
It seems you want one model per dropdown. See the optional parameter on http://knockoutjs.com/documentation/observables.html#activating_knockout
You can then bind to each dropdown a separate model via something like ko.applyBindings(viewModel, $('##ddname')[0]);
I don't understand what this blurb is trying to do:
value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue
It seems you're trying to use two different models to control the dropdown. I am fairly certain that won't end well. Try using one model to rule them all to handle all the dropdowns. I think you'll find it much easier to maintain and logic about.
I am using JQuery Mobile, Javascript, HTML, JSON, AJAX, PHP and MySQL to dynamically display values from the database onto a webpage.
I have a few elements working together. First a drop down menu that populates a list view. The drop down menu elements are obtained from the database. This is done via the "on change" method, when activated it passes the selected option and the ID of the drop down menu to a JSON function, which takes care of retrieving the data from the database with the help of PHP and functions. I also have a radio button that works as a filter to the type of results that are to be retrieved from the database and displayed on the list view.
My problem is hopefully simpler than it looks. I am trying to pass my JSON function the value of the selected option from the drop down menu. I know that the JSON wants an object, but I am having problems doing this.
To illustrate how I try to do this, let's say I have a drop down list with fruits, I have a radio button that allows you to choose from Red fruits or Green fruits, and a list view to display my results:
<div data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="horizontal" data-mini="true">
<input type="radio" name="radio" id="red" value="1" checked="checked" onchange="change_fruit_color('red');"/>
<label for="red">Red Fruits</label>
<input type="radio" name="radio" id="green" value="2" onchange="change_fruit_color('green');"/>
<label for="green">Green Fruits</label>
</fieldset>
</div>
<li>
<label for="fruit" class="select">Fruits</label>
<select name="fruit" id="fruit" data-mini="true" onclick="populate_list('fruit', this);" onchange="clear_dropdown('fruit'); refresh_dropdown('fruit');">
<option value=""></option>
</select>
</li>
Note that I have a few extra functions, refresh_dropdown() and clear_dropdown() handle refreshing my drop down list items, nothing to worry about there. The populate_list function works good too. The issue is regarding the logic of the change_fruit_color() function:
function change_fruit_color(fruit_color)
{
var selected_value = $("#fruits option:selected").text();
if(selected_value)
{
switch(fruit_color)
{
case "red":
populate_list('red', selected_value);
break;
case "practice":
populate_list('green', selected_value);
break;
}
clear_dropdown("fruit");
refresh_dropdown("fruit");
}
}
function populate_list(selected_fruit, this_menu)
{
var find_fruit_url = server_root_url + 'fruit/find_fruit.php';
var value = this_menu.options[this_menu.selectedIndex].value;
var params = {
control: selected_fruit,
value: value
};
$.ajax({
type: "POST",
url: find_fruit_url,
async:false,
data: params,
success: function(json){
json_populate(json, selected_fruit, value);
}
});
}
As you can see, I can call the populate_list three times; once within the "onclick" function of the drop down list, the other within the change_fruit_color that's called via the "onchange" event in the radio button box. In here is where the issue lies.
I tried to make a sample of my general issue so that I write way less code for all of you to read through. Any help on how to proceed would be greatly appreciated!
You're passing selected_value as the second argument to populate_list, but your second argument (this_menu) is supposed to be the select if you want to get the options property on it. I would rename the this_menu argument to selected_value and use that instead of fetching the value from the select again.
Please post your json_populate function to be check what's going on there.
Maybe a time saver would be how you properly format a JSON array. Remember that you've have to use the following notation when dealind with JSON arrays...
var myJsonArray = {
"name_1": value1,
"name_2": value2
};
So maybe your param's var should go as this:
var params = {
"control": selected_fruit,
"value": value
};
Hope this helps.
Luis M.
I solved a similar issue by using this code:
$.post(urlpath,formData,'json')
.done(function(data) {
$.each(data.collection, function (key, value) {
$("#priorityid").append("<option value='"+value.codemaster_detls_name+"'>" + value.codemaster_detls_name + "</option>");
})
Priorityid is the id of the particular list, codemaster_detls_name is my mysql table name from which I am retrieving data.
I want to say hello to the stackoverflow community.
I've just started using knockout a few days ago.
Right know I'm using it to make a dynamic menu builder for a CMS I'm working on.
Here is the code: http://jsfiddle.net/dnlgmzddr/HcRqn/
The problem is that when I choose an element from the select box, the input field update as I expect, but the observable doesn't reflect the change. Because of that, the add button is not enabled.
What am I missing? How can I fix it?
Thank you.
When you populate the url field, you would need to trigger the change event to get the observable to be upated. So, you could do:
$("#url").val('/pages/' + id).change();
Another option though that is more in the Knockout spirit is to use a binding on your select. In this case, you would likely want to populate an observable with that value, then use a manual subscription to default the formatted value into the input field.
this.itemUrl = ko.observable();
this.selectedUrl = ko.observable();
this.selectedUrl.subscribe(function(newValue) {
if (newValue) {
this.itemUrl("/pages/" + newValue);
}
}, this);
Then, bind your select to selectedUrl:
<select id="pagedList" data-bind="value: selectedUrl">
<option value=""><option>
<option value="test">Test</option>
</select>
Here is a sample: http://jsfiddle.net/rniemeyer/HcRqn/21/
You could also eliminate the extra observable and manual subscription if the "value" of your options was the url.
I can't see anywhere in your code where you are actually enabling the button when a field is selected. So I might be missing something, but just enable the button on change. Like the following:
function LoadMenu() {
$("#pagedList").change(function () {
var id = $(this).val();
$("#url").val('/pages/' + id);
// remove the disabled attribute here
$('button.space').removeAttr('disabled');
});
}