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>
Related
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
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.
I'm trying to create a table where new items will be showed with a different background color. The exemple bellow shows what I'm doing.
var data = [{
name: "Gibson",
id: "1"
}, {
name: "Fender",
id: "2"
}, {
name: "Godin",
id: "3"
}, {
name: "Tagima",
id: "4"
}, {
name: "Giannini",
id: "5"
}];
var list = [{
name: "Gibson",
id: "1"
}, {
name: "Fender",
id: "2"
}];
var ViewModel = function() {
var self = this;
self.mylist = ko.mapping.fromJS(list);
self.data = ko.mapping.fromJS(data);
self.selectedItem = ko.observable(undefined);
self.addItem = function() {
if (self.selectedItem == undefined) return;
self.mylist.push(self.selectedItem());
$("#" + self.selectedItem().id()).addClass("newItem");
self.selectedItem(undefined);
}
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
.newItem {
background-color: #DCEDC1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/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/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<select data-bind="options: $root.data, optionsText: 'name', optionsCaption: 'Select yout Guitar', value: selectedItem, event:{change: $root.addItem}"></select>
<table>
<thead>
<tr>
<th>
<p>
Guitars
</p>
</th>
</tr>
</thead>
<tbody data-bind="foreach:mylist">
<tr data-bind="attr:{id: $data.id}">
<td>
<p data-bind="text: $data.name"></p>
</td>
</tr>
</tbody>
</table>
But in in some views, the new row in the table is added after the function addItem finish so the background color do not change. Is there another way to do it or to workaround this issue?
Using jQuery to keep track of the classes will be difficult if you're also using knockout. If your guitar objects remain as simple as they are, you can implement your feature with very little code (you don't have to create new viewmodels if you don't want to). This is what you'll need to do:
Keep track of all the new items in an array
For each item, determine if they need the .isNew class by checking if they're in this array
Toggle a class using the css binding
Step 1:
Inside ViewModel you can create a third observableArray. myList still stores all selected items, the new array newItems stores just the id properties of the items added through the UI.
Step 2:
In the addItem method, instead of selecting an element via jQuery and adding a class, we push the new item to the newItems array.
Step 3:
Replace the attr binding with the css binding that creates a computed boolean to indicate if the row is new or not:
<tr data-bind="css:{'newItem' : guitarIsInArray($data, $parent.newItems())}">
Additional notes:
You were using selectedValue to store the <select> element's changes: it's better to subscribe to this value's changes than to create another event listener via event: { change: fn }
Since your guitars are just plain objects, the Gibson in the data array will not equal the Gibson in myList. I've created a helper method to make sure you don't get duplicate values (guitarIsInArray).
Eventually, even if you're not creating a Guitar viewmodel, I'd try to make sure there's only one object reference per guitar in your code.
Here's an updated example:
var data = [{
name: "Gibson",
id: "1"
}, {
name: "Fender",
id: "2"
}, {
name: "Godin",
id: "3"
}, {
name: "Tagima",
id: "4"
}, {
name: "Giannini",
id: "5"
}];
var list = [{
name: "Gibson",
id: "1"
}, {
name: "Fender",
id: "2"
}];
var ViewModel = function() {
var self = this;
self.data = ko.mapping.fromJS(data);
self.mylist = ko.mapping.fromJS(list);
self.newItems = ko.observableArray([]);
// This excludes any guitar in mylist from data
self.unusedData = ko.computed(function() {
return self.data().filter(function(guitar) {
return !guitarIsInArray(guitar, self.mylist());
});
});
self.selectedItem = ko.observable();
// Called whenever your select changes
self.selectedItem.subscribe(function(newItem) {
if (!newItem || guitarIsInArray(newItem, self.mylist())) {
return;
}
self.mylist.push(newItem);
self.newItems.push(newItem);
});
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
function guitarIsInArray(guitar, array) {
for (var g = 0; g < array.length; g += 1) {
if (array[g].id() === guitar.id()) {
return true;
}
}
return false;
};
.newItem {
background-color: #DCEDC1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/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/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<select data-bind="options: $root.unusedData, optionsText: 'name', optionsCaption: 'Select your Guitar', value: selectedItem"></select>
<table>
<thead>
<tr>
<th>
<p>
Guitars
</p>
</th>
</tr>
</thead>
<tbody data-bind="foreach:mylist">
<tr data-bind="css:{'newItem' : guitarIsInArray($data, $parent.newItems())}">
<td>
<p data-bind="text: $data.name"></p>
</td>
</tr>
</tbody>
</table>
If you add a duplicate item with the same id then in order to add a class jquery will select the first element with that id from DOM tree (id is a unique selector). Here is what you can do by using knockout
Example :https://jsfiddle.net/9aLvd3uw/213/
HTML :
<select data-bind="options: $root.data, optionsText: 'name', optionsCaption: 'Select yout Guitar', value: selectedItem, event:{change: $root.addItem}"></select>
<table>
<thead>
<tr>
<th>
<p>
Guitars
</p>
</th>
</tr>
</thead>
<tbody data-bind="foreach:mylist">
<tr>
<td>
<p data-bind="text:name, css:CSS"></p>
</td>
</tr>
</tbody>
</table>
VM:
var data = [{name: "Gibson",id: "1"}, {name: "Fender",id: "2"},
{name: "Godin",id: "3"},{name: "Tagima",id: "4"},
{name: "Giannini",id: "5"}];
var list = [{name: "Gibson",id: "1"}, {name: "Fender",id: "2"}];
var ViewModel = function() {
var self = this;
self.mylist = ko.observableArray([]);
self.data = ko.observableArray([]);
self.mylist($.map(list, function (element) {
return new ItemViewModel(element);
}));
self.data($.map(data, function (element) {
return new DataViewModel(element);
}));
self.selectedItem = ko.observable(undefined);
self.addItem = function() {
if (self.selectedItem == undefined) return;
//uncomment below if you want only last added item has that class
//ko.utils.arrayForEach(self.mylist(), function (item) {
// if (item) {
// item.CSS('');
// }
//});
self.mylist.push(new ItemViewModel({name:self.selectedItem().name() , id:self.selectedItem().id() , css:'newItem'}));
self.selectedItem(undefined);
}
}
var ItemViewModel = function (data){
var self = this;
self.name = ko.observable(data.name);
self.id = ko.observable(data.id);
self.CSS = ko.observable(data.css ? data.css :'');
}
var DataViewModel = function (data){
var self = this;
self.name = ko.observable(data.name);
self.id = ko.observable(data.id);
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
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>
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/