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.
Related
I have this following html code for select tags.
<option ng-repeat="option in AllData" value="{{item.Id}}" ng-selected="data.Symbol==item.Id">{{item.Symbol}}</option>
I want to convert this to ng-options. can someone please shed some light on this.
the value of AllData is as follows. It is an array of objects.
0:Object
Id: 1
Symbol: "GR"
1:Object
Id: 2
Symbol: "DR"
I tried something like this but was not successful.
ng-options="item as item .Symbol for item in AllData track by item.Id">
Try this
<select ng-options="option as option.Symbol for option in AllData">
</select>
when using ngOptions, you have to also bind ng-model with the initial value of select.
mention that since you used track by item.Id, the default value should at least contains property Id and meets the one of the items Id property.
refer the below sample:
angular.module("app", [])
.controller("myCtrl", function($scope) {
$scope.AllData = [{
0: Object,
Id: 1,
Symbol: "GR"
}, {
1: Object,
Id: 2,
Symbol: "DR"
}];
//$scope.data = $scope.AllData[0];
$scope.data = {
0: Object,
Id: 1,
Symbol: "GR"
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<select ng-model="data" ng-options="item as item.Symbol for item in AllData track by item.Id"></select> {{selectedItem}}
</div>
I would like to initialize a selection of a select field with ng-options.
Lets assume we've got the following items:
$scope.items = [{
id: 1,
label: 'aLabel',
subItem: { name: 'aSubItem' }
}, {
id: 2,
label: 'bLabel',
subItem: { name: 'bSubItem' }
}];
The following html:
<select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
But now instead of selecting the option by the following command:
$scope.selected = $scope.items[0];
I do not have the object, I have only the information of the id so I like to initialize the select by something like that:
$scope.selected = $scope.otherRandomObject.id
My thought was that using track by item.id is somehow creating the relation between object and the id.
If you want to have a pre-selected value, then you can use ng-selected
<select ng-options="item as item.label for item in items" ng-selected="$first"></select>
In this case, the first item in your item array would be selected
Hope i answered your question
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.
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-->
I have this structure:
model = [
{
name: 'name1',
items: [
{
name: 'subobj1'
},
{
name: 'subobj2'
}]
},
{
name: 'name2',
items: [
{
name: 'subobj1'
},
{
name: 'subobj2'
}]
},
....
]
Question is: How do I write ngOptions attrbiute to output this object like this?:
<select>
<optgroup label="name1">
<label>subobj1</label>
<label>subobj2></label>
</optgroup>
....
</group>
Also - ngRepeat is not an option. I have to do this ngOptions alone for plugin being used to work.
ngOptions doesn't support multi-dimensional arrays. You must flatten your array first.
Read more in this answer.
I used a filter:
app.filter('flatten' , function(){
return function(array){
return array.reduce(function(flatten, group){
group.items.forEach(function(item){
flatten.push({ group: group.name , name: item.name})
})
return flatten;
},[]);
}
})
And the markup:
<select ng-model="select"
ng-options="item.name
group by item.group
for item in model | flatten"></select>
<select>
<option ng-repeat-start="m in model" ng-bind="m.name"></option>
<option ng-repeat-end ng-repeat="item in m.items" ng-bind="item.name"></option>
</select>
You might add something like style="font-weight: bold;" on the first option (which is the group label, by the way) and something like style="padding-left: 15px;" on the second option line, which is another repeat for all the first option line.
So basically by doing this you just add 2 levels (without optgroup tag, mind you) to your select.