KnockoutJS binding to Key/Value pair - javascript

I am trying to bind to key/value pair data with KnockoutJS:
this.personal = {
"name" : "Chuck",
"country" : "USA"
};
In my HTML i use the $data binding:
<ul data-bind="foreach: personal">
<li data-bind="text: $data"></li>
</ul>
which results in:
[object Object]
[object Object]
Does anybody know how my binding should look like if I want to see this:
name: Chuck
country: USA
in other words...how I can show the property name and the property value?
EDIT: Someone pointed me at: https://github.com/jamesfoster/knockout.observableDictionary But I still hope to bind without an extra library

There is an easier way of binding to a key-value pair using Knockout.js. Say you have a key value pair that looks like the following
myItems: [
{ Name: 'Item 1', Value: 1},
{ Name: 'Item 3', Value: 3},
{ Name: 'Item 4', Value: 4}
],
Just use the following html to bind to the key value pair.
<select data-bind="options: myItems, optionsText: 'Name', optionsValue: 'Value'></select>
References:
http://knockoutjs.com/documentation/options-binding.html

Try something like this:
<ul data-bind="keyvalue: properties">
<li>
<span data-bind="text: key"></span> :
<span data-bind="text: value"></span>
</li>
</ul>
For JavaScript:
function AppViewModel() {
this.properties = { b: 'c', d: 'e' };
}
ko.bindingHandlers['keyvalue'] = {
makeTemplateValueAccessor: function(valueAccessor) {
return function() {
var values = valueAccessor();
var array = [];
for (var key in values)
array.push({key: key, value: values[key]});
return array;
};
},
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['foreach']['init'](element, ko.bindingHandlers['keyvalue'].makeTemplateValueAccessor(valueAccessor));
},
'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['foreach']['update'](element, ko.bindingHandlers['keyvalue'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);
}
};
ko.applyBindings(new AppViewModel());

Create a function in your view model that converts the object property names and values into an array of objects with key and value properties containing the aforementioned name and value.
var ExampleViewModel = function () {
var self = this;
self.personal = {
"name": "Loek",
"country": "Netherlands"
};
self.personalList = function () {
var list = [];
for (var i in self.personal) if (self.personal.hasOwnProperty(i)) {
list.push({
"key": i,
"value": self.personal[i]
});
}
return list;
};
};
Your html template should look like the following:
<ul data-bind="foreach: personalList()">
<li data-bind="text: $data.key + ': ' + $data.value"></li>
</ul>
This results in the following output:
name: Loek
country: Netherlands
Here's a fiddle with a working example.

I think you should do
<ul data-bind="foreach: personal">
<li data-bind=" text: country"></li>
<li data-bind=" text: name"></li>
</ul>​
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
// Use an array here
this.personal = [{
"name": "Loek",
"country": "Netherlands"
}];
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());​
fiddle http://jsfiddle.net/Aw5hx/
P.S. i never used knockoutJS before this post so i'm no world expert.

Related

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.

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/

Knockoutjs model does not update when select options change

I have a computed function which doesn't update the UI when a select options change. But works fine if I add or remove a line.
This is the HTML:
<button data-bind="click: add">Add New</button>
<ul data-bind="foreach: items">
<li>
<label data-bind="text: name"></label>
<select data-bind="options: [1,2,3], value: val"> </select>
</li>
</ul>
TOTAL: <span data-bind="text: total"></span>
And this the JavaScritp:
function viewModel (initialItems) {
this.items = ko.observableArray(initialItems);
this.total = ko.computed(function () {
var total = 0;
for (var i = 0; i < this.items().length; i++)
total += this.items()[i].val;
return total;
}, this);
this.add = function() { this.items.push({name: "New", val: 1}); };
}
ko.applyBindings(new viewModel([{name: "Alpha", val: 2},
{name: "Beta", val: 3},
{name: "Gamma", val: 1}]));
And here is the fiddle: http://jsfiddle.net/waUE4/
How can I get the model update when selection change?
Thanks for your help.
Edit
Working version: http://jsfiddle.net/fCE3a/1/
The reason why the val property is not updated is that it is not declared as an Observable property.
Check out this sample code from the official KnockoutJS website, it looks like what you want to do: Cart editor example

knockout js json bind list to clicked listitem

I would like to select a specific location by Continent / Country / State / etc.
I get a JSON and I managed to display/select the Continent, but I cannot get the list of Countries to populate.
I am rather new to knockout and JS, so I am probably doing something wrong (I managed to do this based on the live examples from knockout, but those were using select, and I cannot seem to get it to work on ul)
The JSON (composed by hand, so might have errors)
var Continent = [
{ Name: "Europe", Countries: [{ Name: "England", ID: 1 }, { Name: "Wales", ID: 2 }] },
{ Name: "America", Countries: [{ Name: "US", ID: 3 }, { Name: "Canada", ID: 4 }] },
{ Name: "Asia", Countries: [{ Name: "India", ID: 5 }, { Name: "China", ID: 6 }] }
];
The javascript:
var locationVM = function () {
var self = this;
self.selectedContinent = ko.observable("none");
self.selectedCountry = ko.observable();
self.selectedContinent.subscribe(function () {
self.selectedCountry(undefined);
});
self.onClickContinent = function (data) {
self.selectedContinent(data.Name);
};
};
ko.applyBindings(new locationVM());
The HTML
<div>
<ul data-bind="foreach: Continent">
<li data-bind="text: Name, click: $parent.onClickContinent" />
</ul>
</div>Selected Continent : <span data-bind="text: selectedContinent">text</span>
<div data-bind="with: selectedContinent">
<ul data-bind="foreach: Countries">
<li data-bind="text: Name" />
</ul>
</div>
and here is the fiddle http://jsfiddle.net/norbert/WB46V/
Also, if there is anything I missed, or is not needed, please point it out, or provide link for further studies ;)
You're trying to set the continent value to a string and then later trying to iterate thru the string. What you need to do is set the continent to the data.
Something like this:
self.selectedContinent(data);
Working fiddle here.
http://jsfiddle.net/WB46V/6/
Just to add, I haven't used knockout before, so my solution in the fiddle might not be the best one.

Knockout Binding for Select2 Overwriting Existing Value

I'm using the Knockout.js binding provided by Select2, which is as follows:
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindingsAccessor) {
var obj = valueAccessor(),
allBindings = allBindingsAccessor()
$(element).select2(obj);
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).select2('destroy');
});
},
update: function (element, valueAccessor) {
$(element).trigger('change');
}
};
I have a binding on a hidden element as well:
<input type="hidden" class="bigdrop" id="seriesInput" data-bind="value: seriesID, select2: {minimumInputLength: 4, query: $root.nameQuery, formatResult: $root.ownerFormatResult, formatSelection: $root.ownerFormatSelection }" style="margin-left: 1px; width:340px" />
The issue I'm having is that when the select2 binding is created, it changes the pre-existing value in the observable seriesID to the string "[object Object]". I'm fairly certain it's an issue with the binding handler, but I cannot find a working one anywhere. Has anyone found a fix for this?
I'd need to see more of your code to know why you're having that issue, but here's an example with your code that works fine: http://jsfiddle.net/Y93Wm/
I used this viewmodel code:
ko.applyBindings({
seriesID: ko.observable('series-id'),
nameQuery: function(o) {
o.callback({
more: false,
results: [
{ id: "CA", text: "California" },
{ id: "AL", text: "Alabama" },
{id: 'AA1', text: 'aaaaabc'},
{id: 'AA2', text: 'aaaaabcd'},
{id: 'AA', text: 'aaaaaa'},
{id:'BB', text: 'bbbbbbb'}]
});
},
ownerFormatResult: function(o) { return o.text },
ownerFormatSelection: function(o) { return o.text }
});
Make sure to tell select to how to update your observable. So you need to add a change handler to the init function for when select2 changes value.

Categories

Resources