Setting the initial value in a Knockout select - javascript

I'm having problems getting the initial value of a select to be equal to the value in my knockout model.
http://jsfiddle.net/npearson99/bjwAT/2/
In that fiddle, the group should be "Group 2", but it's not selecting any group.
If I change value: 'SelectedGroupId' to value: 2, it works.
<div data-bind="with: selectedWorkout">
<h3>Current Workout</h3>
Workout Id:
<label data-bind="text: Id"></label>
<br/>Workout Name:
<label data-bind="text: Name"></label>
<br/>Group:
<select data-bind="options: $root.groupList,
optionsText: 'GroupName',
optionsValue: 'Id',
optionsCaption: 'No Group',
value: 'SelectedGroupId'"></select>
function Group(Id, GroupName) {
var self = this;
self.Id = Id;
self.GroupName = GroupName;
}
function Workout(id, name, selectedGroupId) {
var self = this;
self.Id = id;
self.Name = name
self.SelectedGroupId = ko.observable(selectedGroupId);
}
function viewModel() {
var self = this;
self.groupList = ko.observableArray([
new Group(1, 'Group One'),
new Group(2, 'Group Two'),
new Group(3, 'Group Three')]);
self.selectedWorkout = ko.observable(new Workout(4, 'Test Workout', 2));
}
ko.applyBindings(new viewModel());

The value binding takes a reference to the property as a parameter and not a string (so not the property name like the optionsValue or optionsValue).
So the correct usage is:
<select data-bind="options: $root.groupList,
optionsText: 'GroupName',
optionsValue: 'Id',
optionsCaption: 'No Group',
value: SelectedGroupId"></select>
Demo JSFiddle.

Related

Modifying observableArray does not instantly update select UI

I have a multi-select dropdown. If the user selects the option all, I want all the other options to be deselected and only select all. I have this almost working, but my issue is that the select does not show the updated value until minimise the dropdown. The state of the observableArray appears to be correct.
Here is the HTML:
<select data-bind="options: games, selectedOptions: selectedGame, optionsText: 'name', optionsValue: 'id'" multiple="true"></select>
And the javascript:
this.games= [
{
name: 'All',
id: 'all'
},
{
name: 'Game1',
id: 'game1'
},
{
name: 'Game2',
id: 'game2'
},
]
this.selectedGame = ko.observableArray(['all']);
this.selectedGameBeforeChange = ko.observableArray([]);
this.selectedGame.subscribe((oldValue) =>
{
this.selectedGameBeforeChange(oldValue);
}, null, 'beforeChange');
this.selectedGame.subscribe((newValue) =>
{
const newValueAdded = newValue.filter(x => !this.selectedGameBeforeChange().includes(x));
if (newValueAdded.length > 0 && newValueAdded[0] === 'all'){
this.selectedGame.removeAll();
this.selectedGame.push('allCombined');
}
this.updateTable();
});
The code above works, but the change is only reflected in the UI once I have 'minimised' the select and reopen it. Is there a way to force the UI to update as soon my observableArray is updated?
You've got 2 bugs:
Instead of push('allCombined'), it should be push('all').
It works when all is selected last, but not when it's selected as the first option. To fix that, we need to modify the condition a bit.
Here's the final code (with few more minor modifications, e.g using self instead of this):
var vm = function () {
var self = this;
self.games = [
{ name: 'All', id: 'all' },
{ name: 'Game1', id: 'game1' },
{ name: 'Game2', id: 'game2' }
];
self.selectedGames = ko.observableArray(['all']);
self.selectedGamesBeforeChange = ko.observableArray([]);
self.selectedGames.subscribe((oldValue) =>
{
self.selectedGamesBeforeChange(oldValue);
}, null, 'beforeChange');
self.selectedGames.subscribe((newValue) =>
{
if (newValue.length > 1 &&
newValue.includes('all')){
self.selectedGames.removeAll();
self.selectedGamesBeforeChange.removeAll();
self.selectedGames.push('all');
}
});
};
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="options: games, selectedOptions: selectedGames, optionsText: 'name', optionsValue: 'id'" multiple="true"></select>

Set Value of Dynamically Populated Select in Knockout

So I'm using KnockoutJS to populate a <select> with options and to get the value of the select.
<select data-bind="enable: cols1().length > 0, options: cols1(), optionsText: 'name', value: jCol1" id="col1"></select>
The variable cols1 holds objects with the simple format of { name: "name" } just because it needs to be objects for some of the other stuff I do on the page. Is there any way to set the value of the select from outside of the data-binds on this element?
The value part of the binding says:
Store a reference to an item that is in cols1 in jCol1
If you want to change the selection from outside of the UI, you'll have to set jCol1 to a value that is in the cols1 array. If you try to set it to anything else, knockout will reset it to the first value immediately. Switch out the commented lines of code in the example below to see this happen:
var ViewModel = function() {
this.options = ko.observableArray([
{ name: "Item 1" },
{ name: "Item 2" },
{ name: "Item 3" }
]);
this.selection = ko.observable();
this.selection.subscribe(function(newValue) {
console.log(newValue)
});
this.changeSelectionFromOutside = function() {
// This does not work because knockout does not do a
// deep comparison of objects
// this.selection({ name: "Item 3" });
// This _does_ work, because it references one of the
// options objects
this.selection(this.options()[2]);
}.bind(this);
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: options, value: selection, optionsText: 'name'"></select>
<button data-bind="click: changeSelectionFromOutside">
Set option 3
</button>
Now, you can also choose to just store a string ID (or other primitive) of your selection. This makes it easier to set things from the outside, because you only need the ID instead of a reference to the actual item:
var ViewModel = function() {
this.options = ko.observableArray([
{ name: "Item 1" },
{ name: "Item 2" },
{ name: "Item 3" }
]);
this.selection = ko.observable();
this.selection.subscribe(function(newValue) {
console.log(newValue)
});
this.changeSelectionFromOutside = function() {
this.selection("Item 3");
}.bind(this);
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: options, value: selection, optionsText: 'name', optionsValue: 'name'"></select>
<button data-bind="click: changeSelectionFromOutside">
Set option 3
</button>
Let's use the states example:
//list of US states in array
self.usStates = [
{ StateName: 'Alabama', Abbr: 'AL' },
{ StateName: 'Alaska', Abbr: 'AK' },
...
//observable from that array
self.States = ko.observableArray(self.usStates);
//the selected state
self.selectedState = ko.observable();
//set selectedState from some value received from server
self.selectedState(self.States.find("Abbr", { StateName: "", Abbr: '<<Value i.e. TX>>' }).Abbr);
//finds TX, sets state to 'Texas'
//find custom function used to find specific object in array
ko.observableArray.fn.find = function (prop, data) {
var valueToMatch = data[prop];
return ko.utils.arrayFirst(this(), function (item) {
return item[prop] === valueToMatch;
});
};
This may be overly complicated for what you're looking to do, but this is how I do it when I want to choose a value from a select based on a value from the record in the database.

how do you set a value that is an observable in a dropdown?

var vm = (function() {
var selectedFoo = ko.observable(),
foos = [
{ id: 1, fooName: 'fooName1', fooType: 'fooType1' },
{ id: 2, fooName: 'fooName2', fooType: 'fooType2' },
{ id: 3, fooName: 'fooName3', fooType: 'fooType3' },
];
return {
selectedFoo: selectedFoo,
foos: foos
};
}());
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: foos,
optionsText: 'fooName',
optionsCaption: 'Select foo',
value: selectedFoo"></select><br />
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
While above code works, how would set the initial value of the dropdown? Say you got an id value of 2 from an ajax call. How would you set the selected option based on the id?
I've looked in the for solutions but I only found adding a optionsValue but I need the member of the selected option as display
Any help would be much appreciated.
You're misundertanding something. I've added the selected value in your snippet, and, if you change the id, the select list is updated correcty, and you still display what you want. I've added a bound textbox where you can type the id so that you can check it works as expected.
NOTE: just in case the comment below is what I couldn't understand from your question, I'm implementing a new writable computed observable that allos to use the whole object as selection.
var vm = (function() {
var foos = [
{ id: 1, fooName: 'fooName1', fooType: 'fooType1' },
{ id: 2, fooName: 'fooName2', fooType: 'fooType2' },
{ id: 3, fooName: 'fooName3', fooType: 'fooType3' },
],
selectedFoo = ko.observable(),
selectedFooId = ko.computed({
read: function() {
return selectedFoo() ? selectedFoo().id : null;
},
write: function(value) {
var newSel = foos.find(function(f) {return f.id == value;});
selectedFoo(newSel);
}
});
return {
selectedFooId: selectedFooId,
selectedFoo: selectedFoo,
foos: foos
};
}());
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: foos,
optionsText: 'fooName',
optionsCaption: 'Select foo',
value: selectedFoo"></select><br />
<input type=text data-bind="value: selectedFooId, valueUpdate:'keyup'" />
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

Instantiate a property from viewModel within foreach

I am iterating over an object with a knockout's foreach. Inside this foreach I render a table, and each table has a dropdown.
I need the value of the select, however the ko.observable() is not working within the foreach, because it sets each select value simultaneously. I need the individual select value of each field, not set each select to the same value.
Is there a solution to this?
<!--ko foreach: {data: thing, as: 'blah'}-->
<div data-bind="text: JSON.stringify(blah)"></div>
<select data-bind="options: $root.countries, optionsText: 'name', optionsValue: 'id', value: $root.selectedChoice, optionsCaption: 'Choose..'"></select>
<br/>
<input type="button" data-bind="click: $root.sendMe, enable: $root.selectedChoice" Value="Click Me"/>
<!--/ko-->
This is a fiddle that demonstrates with a simple example.
If you have multiple dropdowns, you're going to need multiple observables to store the selected value if you want to save individual selections. For example:
var CountryModel = function (data) {
var self = this;
self.id = ko.observable(data.id);
self.name = ko.observable(data.name);
};
var ViewModel = function (data) {
var self = this;
self.things = ko.observableArray([
{ blarg: 'blarg', selectedChoice: ko.observable() },
{ means: 'means', selectedChoice: ko.observable() },
{ yes: 'yes', selectedChoice: ko.observable() }
]);
self.countries = ko.observableArray([
new CountryModel({ id: "1", name: "Russia" }),
new CountryModel({ id: "2", name: "Qatar" })
]);
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<!--ko foreach: {data: things, as: 'thing'}-->
<div data-bind="text: ko.toJSON(thing)"></div>
<select data-bind="options: $root.countries,
optionsText: 'name',
optionsValue: 'id',
value: selectedChoice,
optionsCaption: 'Choose..'">
</select>
<hr>
<!--/ko-->

problems binding to observable array in knockoutJS

I am currently having problems binding data to an observable array in knockoutJS. What I am trying to do is display new values based on the user's selection from a select box.
The fiddle is available at http://jsfiddle.net/jwayne2978/k0coh1fz/3/
My HTML looks like the following.
<select data-bind="options: categories,
optionsText: 'name',
optionsValue: 'id',
value: selectedCategory,
optionsCaption: 'Choose...',
event: { change: categoryChanged }
">
<div data-bind="foreach: values">
<div data-bind="text: name"></div>
</div>
<div data-bind="foreach: categories">
<div data-bind="text: name"></div>
</div>
My JavaScript looks like the following.
var categories = [
{ "name" : "color", "id": "1" },
{ "name" : "names", "id": "2" }
];
var values0 = [ { "name" : "empty1" }, { "name" : "empty2" } ];
var values1 = [ { "name" : "white" }, { "name" : "black" } ];
var values2 = [ { "name" : "john" }, { "name" : "name" } ];
var Category = function(data) {
this.name = ko.observable(data.name);
this.id = ko.observable(data.id);
};
var Value = function(data) {
this.name = ko.observable(data.name);
}
var ViewModel = function(categories, values) {
var self = this;
self.categories = ko.observableArray(ko.utils.arrayMap(categories, function(category) {
return new Category(category);
}));
self.selectedCategory = ko.observable();
self.values = ko.observableArray(ko.utils.arrayMap(values, function(value) {
return new Value(value);
}));
self.categoryChanged = function(obj, event) {
if(self.selectedCategory()) {
console.log(self.selectedCategory());
if("1" == self.selectedCategory()) {
//console.log(values1);
self.values.push(new Value({"name":"test1"}));
} else if("2" == self.selectedCategory()) {
//console.log(values2);
self.values.push(new Value({"name":"test2"}));
}
}
};
};
var viewModel;
$(document).ready(function() {
viewModel = new ViewModel(categories, values0);
ko.applyBindings(viewModel);
});
When a category is changed, what I really want to do is something like this.
self.values.removeAll();
for(var v in values1) {
self.values.push(new Value(v));
}
But that doesn't work and so I simply have the line to push a new value into the observable array.
Also, my iterations on the div for the values and categories are not showing and I am unsure why.
Any idea on what I am doing wrong?
your <select> element is missing a closing tag and causing issues further down in the view.
<select data-bind="options: categories,
optionsText: 'name',
optionsValue: 'id',
value: selectedCategory,
optionsCaption: 'Choose...',
event: { change: categoryChanged }"></select>
updated fiddle: http://jsfiddle.net/ragnarok56/69q8xmrp/

Categories

Resources