ObservableArray to Select Option in KnockOut JS - javascript

I want to turn array to select option via knockout js, I know 3 methods for this case, but none of these work perfectly with what I really want, what I want is:
Set default option Choose an option
get selected value
set attr for options
Each method has own issue, but last method has default option and can get selected value, but can't set attr, any idea?
Method 1:
Error:
Uncaught Error: The binding 'value' cannot be used with virtual
elements
Status: not working
function myfunc() {
var self = this;
self.estimate = ko.observableArray([]);
self.selectedValue = ko.observable();
var obj = [{
method_title: "blah blah",
price: "1000"
},
{
method_title: "blah blah 2",
price: "2000"
}
];
self.estimate(obj);
self.selectedValue.subscribe(function(value) {
alert(value);
});
}
ko.applyBindings(new myfunc());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select id="method">
<option value="0">Choose an option</option>
<!-- ko foreach: estimate, value: selectedValue -->
<option data-bind="text: method_title,
attr: { 'data-price': price, 'value': method_title },
text: method_title"></option>
<!-- /ko -->
</select>
Method 2:
Status: working but I could not add default option, it looped everytime.
function myfunc() {
var self = this;
self.estimate = ko.observableArray([]);
self.selectedValue = ko.observable();
var obj = [{
method_title: "blah blah",
price: "1000"
},
{
method_title: "blah blah 2",
price: "2000"
}
];
self.estimate(obj);
self.selectedValue.subscribe(function(value) {
alert(value);
});
}
ko.applyBindings(new myfunc());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select id="method" data-bind="foreach: estimate,value: selectedValue">
<option value="0">Choose an option</option>
<option data-bind="text: method_title,attr: {'data-price': price, value: method_title}"></option>
</select>
Method 3:
Status: working but I could not set attr
function myfunc() {
var self = this;
self.estimate = ko.observableArray([]);
self.selectedValue = ko.observable();
var obj = [{
method_title: "blah blah",
price: "1000"
},
{
method_title: "blah blah 2",
price: "2000"
}
];
self.estimate(obj);
self.selectedValue.subscribe(function(value) {
alert(value);
});
}
ko.applyBindings(new myfunc());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select id="method" data-bind="value: selectedValue,options: estimate,
optionsText: 'method_title',
optionsValue: 'method_title',
optionsCaption: 'Choose an option'"></select>

Your first method had the most promise and so I have corrected that. You don't need to use the value binding in the foreach loop. It has to be used in the <select>, and it works fine.
function myfunc() {
var self = this;
self.estimate = ko.observableArray([]);
self.selectedValue = ko.observable();
var obj = [{
method_title: "blah blah",
price: "1000"
},
{
method_title: "blah blah 2",
price: "2000"
}
];
self.estimate(obj);
self.selectedValue.subscribe(function(value) {
console.log(value);
});
}
ko.applyBindings(new myfunc());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select id="method" data-bind="value: selectedValue">
<option value="0">Choose an option</option>
<!-- ko foreach: estimate -->
<option data-bind="text: method_title,
attr: { 'data-price': price, 'value': method_title }"></option>
<!-- /ko -->
</select>

You just need a little bit modification with your 3rd method.
From knockout official documentation Knockout: The "options" binding you can use optionsAfterRender Parameter. I have modified your code. See if it helps
function myfunc() {
var self = this;
self.estimate = ko.observableArray([]);
self.selectedValue = ko.observable();
var obj = [{
method_title: "blah blah",
price: "1000",
href: "href 1",
title: "go to href 1"
},
{
method_title: "blah blah 2",
price: "2000",
href: "href 2",
title: "go to href 2"
}
];
self.setOptionAttr = function(option, item) {
if(item)
{
ko.applyBindingsToNode(option, {attr: {href:item.href,title:item.title}}, item);
}
}
self.estimate(obj);
self.selectedValue.subscribe(function(value) {
alert(value);
});
}
ko.applyBindings(new myfunc());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select id="method" data-bind="value: selectedValue,options: estimate,
optionsText: 'method_title',
optionsValue: 'method_title',
optionsCaption: 'Choose an option',
optionsAfterRender: setOptionAttr"></select>

Related

how to set selected option in asp.net mvc using knockout.js?

I'm trying to set an option selected by default based on value recieved from Model.
I'm using asp.net mvc and knockout js for data binding.
//Model.TestValue="DEF"
script section.
<script>
var model = {
MyData: ko.mapping.fromJS(#Html.Raw(Json.Serialize(Model)))
};
ko.applyBindings(model);
</script>
View Section: Razor
#{
var mydropdownlist = new SelectList(
new List<SelectListItem>
{
new SelectListItem {Text = "ABC", Value = "1"},
new SelectListItem {Text = "DEF", Value = "3"},
new SelectListItem {Text = "GHI", Value = "5"}
}, "Value", "Text");
}
View Section HTML.
<select data-bind="options: mydropdownlist, optionsText:'text', value:MyData.testValue "></select>
Now mydropdownlist is populationg fine but I couldn't set "DEF" selected by default.
Set the observable property MyData.testValue, with the required option reference from the mydropdownlist.
MyData.testValue(mydropdownlist.Items()[1]); //DEF
Example:
$(function() {
var VmClass = function() {
self.MyOptions = ko.observableArray([
{ Name: 'Jhon', Age: 45 }, { Name: "Peter", Age: 67 }, { Name: 'Oliver', Age: 90}
]);
self.SelectedOption = ko.observable();
// can select after binding initialized as well.
self.ClickMeToSelect = function() {
self.SelectedOption(self.MyOptions()[2]);
};
self.ClickMeToSelect();
};
var vmInstance = new VmClass();
ko.applyBindings(vmInstance, document.getElementById('[element-id]'));
});
Is this what you are trying to achieve?
var data = [
{"text": "ABC","value": 1},
{"text": "DEF","value": 2},
{"text": "GHI","value": 3},
{"text": "JKL","value": 4},
];
var model = {
mydropdownlist: ko.observableArray(data),
selectedOption: ko.observable(data[1])
};
ko.applyBindings(model);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="options: mydropdownlist, optionsText:'text', value:selectedOption "></select>
<pre data-bind="text: ko.toJSON($root.selectedOption)"> </pre>

Change select drop-down option based on input value using autocomplete in Knockout JS

Using Knockout JS, when a user types into an input field and selects a value (ex: Fruit) using jquery-ui autocomplete selection, I'm trying to change the select drop-down of options in a separate drop-down.
Example scenario:
(1) User begins to type "Fru.."
(2) Selects "Fruit" in autocomplete input field.
(3) Dropdown options changes based on the value: "Fruit"
(4) Dropdown options only shows "Apples" or other options with equals id (ex: ABC)
Autocomplete Input Field
HTML
<input type="text"
id="searchItem"
placeholder="Search"
tabindex="0"
data-bind="textInput: searchItem, valueUpdate: 'onkeyup'"/>
ViewModel/JQuery (Autocomplete)
// Search Item
$(function() {
var searchItem = [
{ id: "ABC", name: "Fruit" },
{ id: "DEF", name: "Animal" },
{ id: "GHI", name: "Color" },
{ id: "JKL", name: "Clothing" }
];
$("#searchItem").autocomplete({
source: searchItem
});
});
Select dropdown
HTML
<select class="form-control"
id="alphabetList"
data-toggle="tooltip"
tabindex="0"
data-bind=" foreach: { data: alphabetList, as: 'item' }, value: selectedItem">
<option data-bind=" attr: { 'value': item.id }, text: item.name"></option>
</select>
ViewModel
// Alphabet List
this.alphabetList = ko.observableArray([
{ id: "ABC", name: "Apples" },
{ id: "DEF", name: "Dog" },
{ id: "GHI", name: "Green" },
{ id: "JKL", name: "Jacket" }
]);
On selection of an item in autocomplete, populate an observable called selectedId. Then create a computed property which filters the alphabetList based on selectedId
You have not mentioned where this autocomplete code exists. But, since you have mentioned ViewModel, I'm assuming you have access to the viewModel's instance in your jquery code.
Also, you don't need to use the foreach binding for displaying options. You can use options binding.
Here's a working snippet with all these changes:
var viewModel = function() {
var self = this;
self.selectedAlphabet = ko.observable();
self.selectedId = ko.observable();
self.searchItem = ko.observable();
self.alphabetList = ko.observableArray([
{ id: "ABC", name: "Apples" },
{ id: "DEF", name: "Dog" },
{ id: "GHI", name: "Green" },
{ id: "JKL", name: "Jacket" }
]);
// this gets triggerred everytime selectedId changes
self.availableAlphabetList = ko.pureComputed(() => {
return self.alphabetList().filter(item => item.id == self.selectedId());
});
}
// I have created an instance to use it in jquery code
var instance = new viewModel();
ko.applyBindings(instance);
$(function() {
var searchItem = [
{ id: "ABC", name: "Fruit" },
{ id: "DEF", name: "Animal" },
{ id: "GHI", name: "Color" },
{ id: "JKL", name: "Clothing" }];
$("#searchItem").autocomplete({
// creating an array with a "label" property for autocomplete
source: searchItem.map(function(item) {
return {
label: item.name,
id: item.id
}
}),
// on select populate the selectedId
select: function(event, ui) {
// if this jquery code is within viewModel, then use "self.selectedId"
instance.selectedId(ui.item.id)
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/themes/base/jquery-ui.css">
<input type="text" id="searchItem" placeholder="Search" tabindex="0" data-bind="textInput: searchItem, valueUpdate: 'onkeyup'" />
<select class="form-control" id="alphabetList" data-toggle="tooltip" tabindex="0" data-bind="options: availableAlphabetList,
optionsText: 'name',
optionsValue: 'id',
value: selectedAlphabet
optionsCaption: 'Choose..'">
</select>
You can also go through this question which has good answers for creating a custom binding for autocomplete

How to combine templates and radio buttons in knockoutjs scripts?

I am trying to show a group of radio buttons using knockoutjs. I also want to have one of the radio buttons selected based on a certain value and know the value of the selected radio button in case someone selects a different item.
This is the code I am trying to use:
<form>
<div data-bind="template: { name: 'items-template', foreach: items }"></div>
<h3 data-bind="text: selectedItem"></h3>
</form>
</body>
<script type="text/html" id="items-template">
<input type="radio" name="skuitem" data-bind="value: id, checked: selectedItem"><span data-bind="text: name"></span><br /></input>
</script>
<script type="text/javascript">
function ItemsViewModel() {
var _this = this;
_this.items = ko.observableArray();
_this.items.push({id: "1", name: "Item A"});
_this.items.push({id: "7", name: "Item B"});
_this.items.push({id: "10", name: "Item C"});
_this.selectedItem = ko.observable("1");
}
ko.applyBindings(new ItemsViewModel());
</script>
The problem is that I get an error in the console:
ReferenceError: selectedItem is not defined
From what I understand, knockout looks into the objects contained in the items array for "selectedItem" instead of binding to the "selectedItem" variable in my ItemsViewModel.
How can I bind my radio buttons to the selectedItem observable when I'm using templates ?
Inside your template, your binding context is an item, you'll only have id and name available to link to directly.
Any properties in the ItemsViewModel can be accessed using $parent or $root.
So, you'll have to write: data-bind="value: id, checked: $parent.selectedItem"
function ItemsViewModel() {
var _this = this;
_this.items = ko.observableArray();
_this.items.push({
id: "1",
name: "Item A"
});
_this.items.push({
id: "7",
name: "Item B"
});
_this.items.push({
id: "10",
name: "Item C"
});
_this.selectedItem = ko.observable("1");
}
ko.applyBindings(new ItemsViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<form>
<div data-bind="template: { name: 'items-template', foreach: items }"></div>
<h3 data-bind="text: selectedItem"></h3>
</form>
<script type="text/html" id="items-template">
<label>
<input type="radio" data-bind="value: id,
checked: $parent.selectedItem" />
<span data-bind="text: name"></span>
</label>
</script>
Instead of solving the issue in the view, you can also solve it in your models. You can pass a reference to the selection observable to each item:
function ItemsViewModel() {
var _this = this;
_this.items = ko.observableArray();
_this.selectedItem = ko.observable("1");
_this.items.push({
id: "1",
name: "Item A",
selectedItem: _this.selectedItem
});
_this.items.push({
id: "7",
name: "Item B",
selectedItem: _this.selectedItem
});
_this.items.push({
id: "10",
name: "Item C",
selectedItem: _this.selectedItem
});
}
ko.applyBindings(new ItemsViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<form>
<div data-bind="template: { name: 'items-template', foreach: items }"></div>
<h3 data-bind="text: selectedItem"></h3>
</form>
<script type="text/html" id="items-template">
<label>
<input type="radio" data-bind="value: id,
checked: selectedItem"/>
<span data-bind="text: name"></span>
</label>
</script>

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-->

Categories

Resources