Dont update all select boxes - knockout js - javascript

Consider the following:
self.selected = ko.observable();
self.selectionItems = ko.observableArray([{name: "bob"}, {name: "sally"}]);
self.containers = ko.observableArray([{name: "container 1", key:1}, {name: "container 2", key: 2}]);
If I have the following html:
<div data-bind="foreach: containers">
<div>
<select data-bind="options: $parent.selectionItems, optionsText: 'name', optionsValue: 'name', value: $parent.selected"></select>
</div>
</div>
Then when one of the containers has an item selected all the containers update to reflect the fact that in container x you selected a name of bob.
How do I make actions like observables specific to that container element?
essentially, if I have 20 containers via the self.containers each container should observe it own actions. Would I make self.selected into an observableArray ?

If you bind the value of every select to the same observable, they're all going to have the same value. You would need to have an observable in the containers object to store the selected.
self.containers = ko.observableArray([{
name: "container 1",
key: 1,
selected: ko.observable()
}, {
name: "container 2",
key: 2,
selected: ko.observable()
}]);
Alternatively, you could have an observableArray of selecteds in the parent, but that would require you to coordinate the size of your array to be the same length as containers and you'd have to use $index to get the right one; it's a clunkier option.

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.

Angular Select initial value not selected

I have a strange problem with data-binding on select.
Here are the definitions of my objects :
brands =
[
{
$$hashKey: "object:18"
firm: Object
id: 242
name: " CONTAGEL "
regex: null
},
...
]
products =
[
{
$$hashKey: "object:7"
brand:
{
$$hashKey: "object:613"
firm: Object
id: 32
name: "Level Junior Solaire"
regex: null
}
label: "Level Junior Solaire - LL LEJUS1501"
productCode: "01646554"
},
...
]
I have a list of brand and a list of products. Each product have a brand (pulled from the brand array).
I want to display the list of product with a select for changing the brand.
<div ng-repeat="p in products">
Product : {{p.productCode}}
<select ng-model="p.brand" ng-options="b as b.name for b in brands">
</div>
The option list is filled with the brand list but not any value is selected. And when i change the value of the select, it changes the brand of the product.
So I don't understand what I have missed.
The issue is with your ng-options. ng-model on select boxes uses references, so unless the object is the same reference, it's not going to be selected by default. In your case, it's a new object and therefore not the same reference, so in ng-model's eyes, it's not a match. Use track by to match on the id.
ng-options="b.name for b in brands track by b.id"
The brand object from Brands variable is different from the brand object of the products variable as you can see in your example.

Angularjs Multidimensional array and two relative select boxes

I have a multidimensional array that holds the product names and versions.
I want to create an interface that lets the user select the product from a select box, and then the version number in the second select box. The second select box should only show the versions numbers of the product that the user selected in the first select box.
This is my mutidimensional array:
[Object]0:
name: "Product 1"
versions: [Array]0:
number: "1.0"
number: "1.5.2"
1:
name: "Product 2"
versions: [Array]0:
number: "0.0"
number: "0.5"
The user has the option to choose multiple products, so I created an array to hold the users selection.
my controller is setup like this:
app.controller('mainController', function ($scope) {
$scope.products = [{id: 1, name: '', versions: []}];
$scope.packages = [];
$scope.packages[0] = { id: 1, name: 'Product 1', versions: [{number: 1.0}, {number: 1.5}, {number: 2.0}]};
$scope.packages[1] = { id: 2, name: 'Product 2', versions: [{number: 0.1}, {number: 0.2}, {number: 0.3}]};
$scope.addProduct = function(){
var id = $scope.products.length + 1;
$scope.products.push({id: id, name: "", version: []});
};
});
And the select boxes are setup like this with angularjs:
<div ng-repeat="product in products">
<label>Product</label>
<select ng-model="product.product" ng-options="package.name for package in packages" class="form-control"></select>
<label>Version</label>
<select ng-model="product.versions" ng-options="version.number for version in product.versions" class="form-control"></select>
</div>
<button ng-click="addProduct()">Add Product</button>
What I tried to do was setup the ng-options to select the versions object of the current product. But this doesn't work.
I created a jsFiddle of what I currently have:
http://jsfiddle.net/rkyu4rjq/
I would really appreciate any suggestions on how to link the version select box with the product selected.
TIA
Eventhough I'm not really keeping track of which version chosen for each product I fixed your relative select options.
You can find a solution here. This should get you on your way!
I missed something very simple.
In the version select box binding I should of used version.number for version in product.product.versions instead of version.number for version in product.versions
Here is a working jsFiddle: http://jsfiddle.net/rkyu4rjq/2/

Knockoutjs Dropdown Pre Selection In Nested Arbitary Javascript Object Working Fine KO 2x versions Not working in KO 3x versions

so this my sample data. which il be loading form server in json format construct into below looking objec graph.
Its an array of "Choice" objects each will be having id, name, stages & currentStageId properties. "stages" property in "Choice" object is an array of "Stage" object which would be having id, name &
value properties.The "Choice" object will go through no.of stages from "First Stage" to "Fourth Stage" so user can select a "Stage" from the give dropdown list and save it. "currentStageId" is the property which stores "id" of stage object which will give in which stage the respective "Choice" object is in
Note: each choice can have different types of stages for brevity kept simple as possible
i.e for Choice 1 the current saved stage is 4
var data = [
new Choice({
id: 1,
name: "One",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 4
}),
new Choice({
id: 2,
name: "Two",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 3
}),
new Choice({
id: 3,
name: "Three",
stages: [
new Stage({
id: 1,
name: "First Stage",
value: 25
}),
new Stage({
id: 2,
name: "Second Stage",
value: 50
}),
new Stage({
id: 3,
name: "Third Stage",
value: 75
}),
new Stage({
id: 4,
name: "Fourth Stage",
value: 100
})],
currentStageId: 2
})];
Here is "Choice" & "Stage" Modles to hold data and ViewModel for binding
function ViewModel(data) {
var self = this;
self.choices = ko.observableArray(data);
//dont require pre selection so kept it with empty observable so it
//will be set to first item in the dropdown list
self.selectedChoice = ko.observable();
}
function Choice(data) {
//debugger;
this.id = data.id;
this.name = data.name;
//require pre selection of stage as choice can go through no of
//stages and selected stage name and value will be stored
this.selectedStage = ko.observable(ko.utils.arrayFirst(data.stages, function (item) {
return item.id === data.currentStageId;
}));
this.stages = ko.observableArray(data.stages);
}
function Stage(data) {
this.id = data.id;
this.name = data.name;
this.value = data.value;
}
ko.applyBindings(new ViewModel(data));
Here is my view
<select data-bind="options: choices, optionsText: 'name', value: selectedChoice"></select>
<select data-bind="options: selectedChoice().stages, optionsText: 'name', value: selectedChoice().selectedStage"></select>
Knockout.js 2x version
Pre selection of saved stage is working
Selected stage for choice is updated into underlying observable
Here is the Working sample with KO 2x in js
Knockout.js 3x version
Pre selection of saved stage is not working
Selected stage for choice is not preserved. When choice is changed the selectedStage is set to first item in the dropdown list each and every time the choice is changed.
Here is the Working sample with KO 3x
Finally the actual part. Question!
Why the same code behaving differently with two different versions of KO. Am i missing something new in KO? or is it a bug in KO?
What code changes should be done to produce same fucntionality as in with later version of KO, using the latest version of KO? because my project is being developed with latest version konckout.js 3.1.0 and i don't wanna switch back to older version for this functionality.
Which behavior of KO version is correct whether 2x or 3x? What is happening internally? which is causing this behavior discrepancies?
Thanks in advance.
I think it is related to 2. Bindings are now refreshed independently
Now you should use selectedChoice move out from options binding, e.g.
<div data-bind="with: selectedChoice">
<select data-bind="options: stages, optionsText: 'name', value: selectedStage"></select>
</div>

knockout.js selectedOptions is not updated

I'm struggling with knockout.js selectedOptions binding.
I fill multiselect with items from observableArray A, choose some, store result in observableArray B. When item gets removed from array A, the B array is not updated.
Is this knockout issue or am I doing something wrong?
HTML code:
<h4>All items:</h4>
<div data-bind="foreach: items">
<p data-bind="text: name"></p>
<button data-bind="click: $parent.remove">Remove item</button>
</div>
<select multiple="multiple" data-bind="
options: items,
selectedOptions: selectedItems,
optionsText: 'name',
optionsCaption: 'Choose one or more...'
"></select>
<h4>Selected items:</h4>
<div data-bind="foreach: selectedItems">
<p data-bind="text: name"></p>
</div>
Javascript:
var viewModel = {
items: ko.observableArray([
{ name: "Item 1", id: "1" },
{ name: "Item 2", id: "2" },
{ name: "Item 3", id: "3" }
]),
selectedItems: ko.observableArray(),
remove: function(item) {
viewModel.items.remove(item);
}
}
ko.applyBindings(viewModel);
Here's the fiddle: http://jsfiddle.net/3FYAe/
How to reproduce:
select one or more items in the multiselect field, they appear in the list below ("Selected items")
remove one of the selected items
the selectbox is updated
4.
Expected: "Selected items" is updated
Actual: "Selected items" keeps deleted values
Answering my own question:
The trivial solution would be to remove the item from selectedItems array as well, i. e.
remove: function(item) {
viewModel.items.remove(item);
viewModel.selectedItems.remove(item);
}
Updated fiddle: http://jsfiddle.net/3FYAe/1/
However, I would like to find a nicer solution as I'm dealing with many more lists and many more dependencies; this is just a simplified example.

Categories

Resources