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.
Related
I want to update the observable array in knockout binding handler. But it is not updating. The following code i tried but nothing worked out.
this.DropdownValues = ko.observableArray([
{ id: 0, type: "Arc",Checked:false },
{ id: 1, type: "Eve",Checked:false },
{ id: 2, type: "Ca",Checked:false },
{ id: 3, type: "test",Checked:false },
]);
Code I have written inside binding handler.
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
console.log("true");
valueUnwrapped.map(function(item){
item[Checked]= true; return item;
});
ko.utils.unwrapObservable(value(valueUnwrapped));
But my view still not detecting the values. foreach not refeshing in view.
You were missing quotes around the word Checked. It should be item['Checked'] or item.Checked rather than item[Checked].
ko.bindingHandlers.updateArray = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
$("#before").text(JSON.stringify(valueUnwrapped));
console.log("true");
valueUnwrapped.map(function(item){
item['Checked']= true; return item;
});
ko.utils.unwrapObservable(value(valueUnwrapped));
}
}
var viewModel = function(){
var self = this;
self.DropdownValues = ko.observableArray([
{ id: 0, type: "Arc",Checked:false },
{ id: 1, type: "Eve",Checked:false },
{ id: 2, type: "Ca",Checked:false },
{ id: 3, type: "test",Checked:false },
]);
};
ko.applyBindings(new viewModel());
<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/3.4.2/knockout-min.js"></script>
Before:<br>
<span id="before"></span>
<div data-bind="updateArray: DropdownValues">
</div>
<br><br>
After:<br>
<span data-bind="text: ko.toJSON(DropdownValues)"></span>
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.
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've got an associative array with jQuery objects that I want to loop over to set a value on each.
I'm relatively new to JavaScript / jQuery, but this is what I tried without any luck:
var inputs = [
{ key: "firstName", val: $("#signup input[name=firstName]") },
{ key: "lastName", val: $("#signup input[name=lastName]") },
{ key: "email", val: $("#signup input[name=email]") },
{ key: "confirmationEmail", val: $("#signup input[name=confirmationEmail]") },
{ key: "password", val: $("#signup input[name=password]") },
{ key: "terms", val: $("#signup input[name=terms]") }
];
inputs.each(function() {
$(this).val("test");
});
In newer browsers you don't need jQuery for that:
inputs.forEach(function(input) {
console.log("key is " + input.key);
input.val.val("test");
});
The .forEach() method is built-in. If you must use jQuery.s.each()` it'd look like this:
$.each(inputs, function(index, input) {
console.log("key is " + input.key);
input.val.val("test");
});
Note that the jQuery $.each() calls the callback function with the index of the element as the first argument and the element itself as the second. The native .forEach() passes those in reverse order.
Finally just as a note, you can set all the <input> values under the "signup" element with
$("#signup input").val("test");
Try this:
$.each(inputs, function(){ $(this).val("test")});
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.