How to refresh the 'options' binding in an ko multi select component? - javascript

I have a multi select component which I have bounded with an 'options' binding, the 'options' get refreshed based on the value I select in another multi select component.
Below is the first multi select component
<select data-bind="multiple: true, required: true, options:repositoriesForSelect, value: selectedRepository"></select>
Based on the value selected in this component, am refreshing the options of the second component
<select data-bind=" multiple: true,required: true,options: branchesForSelect,value: selectedBranch"></select>
using the computed variables to refresh the 2nd options:
branchesForSelect = ko.computed(function(){
//selectedRepository is an observable array here
//some logic
});
Which works fine, but in addition to the above, I want to refresh the 'branchesForSelect' based on the values selected in the same component. Meaning, if the 'branchesForSelect' contains values 'A', 'B', 'C', then on select of 'A', I want to refresh 'branchesForSelect' to show only 'C' in the list of options.
Can someone please guide me? please let me know in comments if the question is unclear.
Thanks

You're on the right track by making the second option list a computed. This is what you still need to do: Inside the computed, use selectedRepository's value to return an array of options that are linked to the selection. By using this value, knockout will make sure that after the value variable of the first select changes, the second option list is reevaluated.
After clarification of question in comments:
Changing the values of the select itself after user input is a bad idea from a UX perspective (if you ask me), but can certainly be done. The code below will show you how. When the first option of a multi-select is active, the others get hidden.
Here's an example:
var repositoryBranches = {
a: ["All", 1, 2, 3, 4, 5, 6],
b: ["All", 0, 7, 8],
c: ["All", 9, 10]
};
var VM = function() {
var self = this;
this.repositoryKeys = ["a", "b", "c"];
this.selectedRepository = ko.observable("a");
this.selectedBranches = ko.observableArray([]);
this.branchesForSelectedRepository = ko.computed(function() {
var allBranchesForRepo = repositoryBranches[self.selectedRepository()];
// We're making an exception if the first one is selected:
// don't show any other options if the selected one is the first one
if (self.selectedBranches()[0] === allBranchesForRepo[0]) {
return ["All"];
}
return allBranchesForRepo;
});
// Clear selection when changing repository
this.selectedRepository.subscribe(function clearSelection() {
self.selectedBranches([]);
});
};
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options:repositoryKeys, value: selectedRepository"> </select>
<select multiple="true" data-bind="options:branchesForSelectedRepository, selectedOptions: selectedBranches"></select>
(fiddle)
Let me know if you need help finding a nice way to link "branches" to "repositories". Ideally, the "repositories" have their own models that contain an array of "branches". You'd then be able to define your computed array something like return self.firstSelection().branches;

Related

extjs - How to filter grid data except selected value in combo box

Ext js has in built function for Grid store called filter and which filter grid result with selected value from combobox.
I want reverse of it. It should filter grid data except selected data.
Example: By default, All check box are selected at first. When I uncheck any checkbox than grid should be show data except that selected checkbox.
Please find screenshot for filter options
Following is the code which I have tried but it does filter grid with selected checkbox.
var filterArray = [];
filterArray.push({
id: 'h2',
property: 'vehicle_trafic_light',
value: 'Y', //For Yellow-Ball
anyMatch: true,
ensitive: false
});
filterArray.push({
id: 'h2',
property: 'vehicle_trafic_light',
value: 'G', //For Green-Ball
anyMatch: true,
ensitive: false
});
store.filter(filterArray);
Let me know if anyone have any suggestion for it.
You can use filterFn for that.
You can user filterBy
filterBy takes a function (lets call is fun) as argument and function fun is called for each record in store.
store.fliterBy(function(record){
if(condition to include record)
return true; // record will be included
else
return false; // record will be excluded
});
And records are filtered based on return value of fun for that record.
So,
If fun return true for record A, then record A will be included in store.
If fun returns false for record B , then record B will be excluded.

How to bind a value in select box which doesn't present in content using Ember.Select

I have a list of values in content array of Ember.Select. The selected value may or may not be present in the content. But have to show the selected value if doesn't present in content.
For more details check this jsbin
In this if I click on "setColorWithNewValue", it sets the colorObj with new value that doesn't present in colorArray. When I click on "alertValue" button it shows the value in ColorObj but it doesn't selected in select box.
The selected object needs to be in the selection too.
When you call setColorWithNewValue, just push the new object to colorsArray and set the reference to colorObj.
setColorWithNewValue: function() {
var newObject = { 'name': 'White', 'value': '5'};
this.get('colorsArray').push(newObject);
this.set('colorObj', newObject);
}
or without changing the colorsArray
mergedArray: function(){
var arr = new Ember.A(this.get('colorsArray'));
arr.push(this.get('colorObj'));
return arr;
}.property('colorObj', 'colorsArray.#each')
And pass merged array to the select field.
Also look into Ember.Array and Ember.Object. Ember extends native Objects and Arrays.

Object equality comparison for input[radio] with ng-model and ng-value

Let me start by saying that this question is very similar to issues with selection in a <select> tag using ng-options. For example, Working with select using AngularJS's ng-options. The specific problem is comparing two different instances of an object which are not reference equal, but which logically represent the same data.
To demonstrate, let's say we have the following array of options and selected option variable in the model:
$scope.items = [
{ID: 1, Label: 'Foo', Extra: 17},
{ID: 2, Label: 'Bar', Extra: 18},
{ID: 3, Label: 'Baz', Extra: 19}
];
$scope.selectedItem = {ID: 1, Label: 'Foo'};
Note that the above objects are just for demonstration. I specifically left off the 'Extra' property on selectedItem to show that sometimes my model objects differ in their specific properties. The important thing is that I want to compare on the ID property. I have an equals() function on my real objects that compares both prototype 'class' and ID.
And then in the view:
<label class="radio inline" ng-repeat="item in items">
<input type="radio" ng-model="selectedItem" ng-value="item"> {{item.Label}}
</label>
Now, the problem here is that the radio button for 'Foo' will not start selected, because angular is using reference equality for the objects. If I changed the last line in my scope to the below, everything would work as expected.
$scope.selectedItem = items[0];
But, the problem I'm having is that in my application, I'm not simply declaring these two simple variables in scope. Rather, the options list and the data structure where the selected option are being bound are both part of larger sets of JSON data that are queried from the server using $http. In the general case, it's very difficult for me to go change the data-bound selected property to be the equivalent option from my data query.
So, my question:
In ng-options for the <select>, angular offers a track by expression that allows me to say something like "object.ID" and inform angular that it should compare the selected model value to the options via the ID property. Is there something similar that I can use for a bunch of radio inputs all bound to the same model property? Ideally, I would be able to tell angular to use my own custom equals() method that I've placed on these model objects, which checks both object type as well as ID. Failing that though, being able to specify ID comparison would also work.
I write a most simple directive. Using a kind of "track-by" to map two different objects. See the http://jsfiddle.net/xWWwT/146/.
HTML
<div ng-app="app">
<div ng-app ng-controller="ThingControl">
<ul >
<li ng-repeat="color in colors">
<input type="radio" name="color" ng-model="$parent.thing" ng-value="color" radio-track-by="name" />{{ color.name }}
</li>
</ul>
Preview: {{ thing }}
</div>
</div>
JS
var app = angular.module('app', []);
app.controller('ThingControl', function($scope){
$scope.colors = [
{ name: "White", hex: "#ffffff"},
{ name: "Black", hex: "#000000"},
{ name: "Red", hex: "#000000"},
{ name: "Green", hex: "#000000"}
];
$scope.thing = { name: "White", hex: "#ffffff"};
});
app.directive('radioTrackBy', function(){
return {
restrict: "A",
scope: {
ngModel: "=",
ngValue: "=",
radioTrackBy: "#"
},
link: function (ng) {
if (ng.ngValue[ng.radioTrackBy] === ng.ngModel[ng.radioTrackBy]) {
ng.ngModel = ng.ngValue;
}
}
};
});
OK, so after further review, I decided to go with a more "mix-in" approach, just replacing the ng-model directive with my own custom directive, in essence. This is very similar to the approach I used for making a "checkbox list" directive based on this answer: https://stackoverflow.com/a/14519881/561604.
.directive('radioOptions', function() {
// Apply this directive as an attribute to multiple radio inputs. The value of the attribute
// should be the scope variable/expression which contains the available options for the
// radio list. Typically, this will be the collection variable in an ng-repeat directive
// that templates the individual radio inputs which have radio-options applied. In addition,
// instead of the normal ng-model, use a selected-option attribute set to the same expression.
// For example, you might use radio-options like this:
// <label ... ng-repeat="item in collection">
// <input type="radio" ... ng-value="item" radio-options="collection" selected-option="myModel.myProperty">
// </label>
//
// See https://stackoverflow.com/questions/19281404/object-equality-comparison-for-inputradio-with-ng-model-and-ng-value
// for the SO question that inspired this directive.
return {
scope: {
radioOptions: '=',
selectedOption: '=',
ngValue: '='
},
link: function( scope, elem, attrs ) {
var modelChanged = function() {
if( jQuery.isArray(scope.radioOptions) ) {
jQuery.each( scope.radioOptions, function(idx, item) {
// This uses our models' custom 'equals' function for comparison, but another application could use
// ID propeties, etc.
if( typeof item.equals === 'function' && item.equals(scope.selectedOption) ) {
elem.prop( 'checked', item === scope.ngValue );
}
});
}
};
scope.$watch( 'radioOptions', modelChanged );
scope.$watch( 'selectedOption', modelChanged );
var viewChanged = function() {
var checked = elem.prop( 'checked' );
if( checked ) {
scope.selectedOption = scope.ngValue;
}
};
elem.bind( 'change', function() {
scope.$apply( viewChanged );
});
}
};
});
As OP requested, here's an example radio button directive that will work with complex objects. It uses underscore.js to find the the selected item from the options. It's a little more complicated than it should be because it also supports loading the options and selected value with AJAX calls.
Why don't you just use the ID for the select like this?
<input type="radio" ng-model="selectedItem" ng-value="item.ID"> {{item.Label}}
And then instead of using selectedItem you could write items[selectedItem].
Oh, and while playing with your problem in jsfiddle I noticed to other things:
a.) You forgot to add a name attribute to the input.
b.) Don't ever use something without a dot in ng-model. If you actually try to output selectedItem with {{selectedItem}} outside the ng-repeat block, you will notice that the value does not update when you chose a radio button. This is due to ng-repeat creating a own child scope.
Since I'm not yet able to add comments, so I have to reply here. Dana's answer worked ok for me. Although I'd like to point out in order to use his approach, one would have to implement the 'equals' function on the objects in the collection. See below example:
.controller('ExampleController', ['$scope', function($scope) {
var eq = function(obj) {
return this.id === obj.id;
};
col = [{id: 1, name: 'pizza', equals: eq}, {id:2, name:'unicorns', equals: eq}, {id:3, name:'robots', equals: eq}];
$scope.collection = col;
$scope.my = { favorite : {id:2, name:'unicorns'} };
}]);
See the plunker link.

Modify source data for ng-repeat

I have an similar app as in the following example and I can't figure out why the source data is not updating. More info is in the example comments. I'm sure this is some trivial issue that I've overlooked.
Controller
$scope.items = [
{ id: 1, color: 'red', title: 'car' },
{ id: 2, color: 'blue', title: 'sky' },
{ id: 3, color: 'transparent', title: 'nothing' }
]
$scope.favoriteIds = [1, 2, 3]
$scope.getItem = function(id) { /* returns an item with given id */ }
Finally, there are two methods to modify $scope.items, but only the first one works, because the new item gets not-already-known id.
$scope.changeData1 = function() {
$scope.items = [{ id: 666, color: 'ugly', title: 'face' }]
$scope.favoriteIds = [666]
}
$scope.changeData2 = function() {
$scope.items = [{ id: 1, color: 'ugly', title: 'face' }]
$scope.favoriteIds = [1]
}
View
<h1>Favourite items</h1>
<ul ng-repeat="id in favoriteIds" data-ng-init="item = getItem(id)">
<li>I like my {{ item.color }} {{ item.title }}</li>
</ul>
<button ng-click="changeData1()">Modify data</button>
<!-- prints: I like my ugly face -->
<button ng-click="changeData2()">Modify nothing</button>
<!-- prints: I like my red car -->
The problem is, that I need to use this second way to modify data.
http://jsfiddle.net/4pEpN/7/
I'm relatively new to Angular as well, so if there's a simple way to do this, I don't know what it is (unfortunately, Angular documentation is atrocious). Regardless, you can avoid this by rethinking the structure of your code (and you'll end up with a better program too).
In your view, you're using ng-init to call getItem on the id during each iteration of your ng-repeat loop. This is what's causing your problem, and it's (apparently) due to an Angular performance feature (more at this question).
Basically, don't use ng-init except to execute something when your app starts. Otherwise, you'll end up with what you've got now: logic in the view (calling getItem(id)) rather than the model, where it belongs.
Instead, use ng-repeat to repeat over the exact data you want to display. Like I said before, this means some code rearrangement. For example, you could use a function to generate the user's current list of items on the fly. Check out this fiddle: http://jsfiddle.net/4pEpN/19/
See my comments in that code for all the changes I made, but the most relevant one is:
$scope.favoriteItems = function() {
var favObjs = [];
for (var i = 0; i < favoriteIds.length; ++i) {
favObjs.push(getItem(favoriteIds[i]));
}
return favObjs;
};
then in your view: <ul ng-repeat="item in favoriteItems()">
There are also lots of other approaches you could use. For instance, you could have an update function, which handles anything that might need to be done after any user input (including updating the user's custom array of items). Then you could call this in your changeData functions.
I don't think ng-init is appropriate since it only affects template initialization.
So how about just calling your getItem(id) for fetching each attribute, like this:
http://jsfiddle.net/JrvbD/1/

How do I set the value property in AngularJS' ng-options?

Here is what seems to be bothering a lot of people (including me).
When using the ng-options directive in AngularJS to fill in the options for a <select> tag, I cannot figure out how to set the value for an option. The documentation for this is really unclear - at least for a simpleton like me.
I can set the text for an option easily like so:
ng-options="select p.text for p in resultOptions"
When resultOptions is for example:
[
{
"value": 1,
"text": "1st"
},
{
"value": 2,
"text": "2nd"
}
]
It should be (and probably is) the most simple thing to set the option values, but so far I just don't get it.
See ngOptions
ngOptions(optional) – {comprehension_expression=} – in one of the
following forms:
For array data sources:
label for value in array
select as label for value in array
label group by group for value in array
select as label group by group for value in array track by trackexpr
For object data sources:
label for (key , value) in object
select as label for (key , value) in object
label group by group for (key, value) in object
select as label group by group for (key, value) in object
In your case, it should be
array = [{ "value": 1, "text": "1st" }, { "value": 2, "text": "2nd" }];
<select ng-options="obj.value as obj.text for obj in array"></select>
Update
With the updates on AngularJS, it is now possible to set the actual value for the value attribute of select element with track by expression.
<select ng-options="obj.text for obj in array track by obj.value">
</select>
How to remember this ugly stuff
To all the people who are having hard time to remember this syntax form: I agree this isn't the most easiest or beautiful syntax. This syntax is kind of an extended version of Python's list comprehensions and knowing that helps me to remember the syntax very easily. It's something like this:
Python code:
my_list = [x**2 for x in [1, 2, 3, 4, 5]]
> [1, 4, 9, 16, 25]
# Let people to be a list of person instances
my_list2 = [person.name for person in people]
> my_list2 = ['Alice', 'Bob']
This is actually the same syntax as the first one listed above. However, in <select> we usually need to differentiate between the actual value in code and the text shown (the label) in a <select> element.
Like, we need person.id in the code, but we don't want to show the id to the user; we want to show its name. Likewise, we're not interested in the person.name in the code. There comes the as keyword to label stuff. So it becomes like this:
person.id as person.name for person in people
Or, instead of person.id we could need the person instance/reference itself. See below:
person as person.name for person in people
For JavaScript objects, the same method applies as well. Just remember that the items in the object is deconstructed with (key, value) pairs.
How the value attributes gets its value:
When using an array as datasource, it will be the index of the array element in each iteration;
When using an object as datasource, it will be the property name in each iteration.
So in your case it should be:
obj = { '1': '1st', '2': '2nd' };
<select ng-options="k as v for (k,v) in obj"></select>
I had this issue too. I wasn't able to set my value in ng-options. Every option that was generated was set with 0, 1, ..., n.
To make it right, I did something like this in my ng-options:
HTML:
<select ng-options="room.name for room in Rooms track by room.price">
<option value="">--Rooms--</option>
</select>
I use "track by" to set all my values with room.price.
(This example sucks: because if there were more than one price equal, the code would fail. So BE SURE to have different values.)
JSON:
$scope.Rooms = [
{ name: 'SALA01', price: 100 },
{ name: 'SALA02', price: 200 },
{ name: 'SALA03', price: 300 }
];
I learned it from blog post How to set the initial selected value of a select element using Angular.JS ng-options & track by.
Watch the video. It's a nice class :)
If you want to change the value of your option elements because the form will eventually be submitted to the server, instead of doing this,
<select name="text" ng-model="text" ng-options="select p.text for p in resultOptions"></select>
You can do this:
<select ng-model="text" ng-options="select p.text for p in resultOptions"></select>
<input type="hidden" name="text" value="{{ text }}" />
The expected value will then be sent through the form under the correct name.
To send a custom value called my_hero to the server using a normal form submit:
JSON:
"heroes": [
{"id":"iron", "label":"Iron Man Rocks!"},
{"id":"super", "label":"Superman Rocks!"}
]
HTML:
<select ng-model="hero" ng-options="obj.id as obj.label for obj in heroes"></select>
<input type="hidden" name="my_hero" value="{{hero}}" />
The server will receive either iron or super as the value of my_hero.
This is similar to the answer by #neemzy, but specifying separate data for the value attribute.
It appears that ng-options is complicated (possibly frustrating) to use, but in reality we have an architecture problem.
AngularJS serves as an MVC framework for a dynamic HTML+JavaScript application. While its (V)iew component does offer HTML "templating," its primary purpose is to connect user actions, via a controller, to changes in the model. Therefore the appropriate level of abstraction, from which to work in AngularJS, is that a select element sets a value in the model to a value from a query.
How a query row is presented to the user is the (V)iew’s concern and ng-options provides the for keyword to dictate what the contents of the option element should be i.e. p.text for p in resultOptions.
How a selected row is presented to the server is the (M)odel’s concern. Therefore ng-options provides the as keyword to specify what value is provided to the model as in k as v for (k,v) in objects.
The correct solution this is problem is then architectural in nature and involves refactoring your HTML so that the (M)odel performs server communication when required (instead of the user submitting a form).
If an MVC HTML page is unnecessary over-engineering for the problem at hand: then use only the HTML generation portion of AngularJS’s (V)iew component. In this case, follow the same pattern that is used for generating elements such as <li />'s under <ul />'s and place a ng-repeat on an option element:
<select name=“value”>
<option ng-repeat=“value in Model.Values” value=“{{value.value}}”>
{{value.text}}
</option>
</select>
As kludge, one can always move the name attribute of the select element to a hidden input element:
<select ng-model=“selectedValue” ng-options=“value.text for value in Model.Values”>
</select>
<input type=“hidden” name=“value” value=“{{selectedValue}}” />
You can do this:
<select ng-model="model">
<option value="">Select</option>
<option ng-repeat="obj in array" value="{{obj.id}}">{{obj.name}}</option>
</select>
-- UPDATE
After some updates, user frm.adiputra's solution is much better. Code:
obj = { '1': '1st', '2': '2nd' };
<select ng-options="k as v for (k,v) in obj"></select>
I have struggled with this problem for a while today. I read through the AngularJS documentation, this and other posts and a few of blogs they lead to. They all helped me grock the finer details, but in the end this just seems to be a confusing topic. Mainly because of the many syntactical nuances of ng-options.
In the end, for me, it came down to less is more.
Given a scope configured as follows:
//Data used to populate the dropdown list
$scope.list = [
{"FirmnessID":1,"Description":"Soft","Value":1},
{"FirmnessID":2,"Description":"Medium-Soft","Value":2},
{"FirmnessID":3,"Description":"Medium","Value":3},
{"FirmnessID":4,"Description":"Firm","Value":4},
{"FirmnessID":5,"Description":"Very Firm","Value":5}];
//A record or row of data that is to be save to our data store.
//FirmnessID is a foreign key to the list specified above.
$scope.rec = {
"id": 1,
"FirmnessID": 2
};
This is all I needed to get the desired result:
<select ng-model="rec.FirmnessID"
ng-options="g.FirmnessID as g.Description for g in list">
<option></option>
</select>
Notice I did not use track by. Using track by the selected item would alway return the object that matched the FirmnessID, rather than the FirmnessID itself. This now meets my criteria, which is that it should return a numeric value rather than the object, and to use ng-options to gain the performance improvement it provides by not creating a new scope for each option generated.
Also, I needed the blank first row, so I simply added an <option> to the <select> element.
Here is a Plunkr that shows my work.
Instead of using the new 'track by' feature you can simply do this with an array if you want the values to be the same as the text:
<select ng-options="v as v for (k,v) in Array/Obj"></select>
Note the difference between the standard syntax, which will make the values the keys of the Object/Array, and therefore 0,1,2 etc. for an array:
<select ng-options"k as v for (k,v) in Array/Obj"></select>
k as v becomes v as v.
I discovered this just based on common sense looking at the syntax.
(k,v) is the actual statement that splits the array/object into key value pairs.
In the 'k as v' statement, k will be the value, and v will be the text option displayed to the user. I think 'track by' is messy and overkill.
This was best suited for all scenarios according to me:
<select ng-model="mySelection.value">
<option ng-repeat="r in myList" value="{{r.Id}}" ng-selected="mySelection.value == r.Id">{{r.Name}}
</option>
</select>
where you can use your model to bind the data. You will get the value as the object will contain and the default selection based on your scenario.
This is how I resolved this. I tracked the select by value and set the selected item property to the model in my JavaScript code.
Countries =
[
{
CountryId = 1, Code = 'USA', CountryName = 'United States of America'
},
{
CountryId = 2, Code = 'CAN', CountryName = 'Canada'
}
]
<select ng-model="vm.Enterprise.AdminCountry" ng-options="country.CountryName for country in vm.Countries track by country.CountryId">
vm is my controller and the Country in the controller retrieved from the service is {CountryId =1, Code = 'USA', CountryName='United States of America'}.
When I selected another country from the select dropdown and posted my page with "Save", I got the correct country bound.
The ng-options directive does not set the value attribute on the <options> elements for arrays:
Using limit.value as limit.text for limit in limits means:
set the <option>'s label as limit.text
save the limit.value value into the select's ng-model
See Stack Overflow question AngularJS ng-options not rendering values.
You can use ng-options to achieve select tag binding to value and display members
While using this data source
countries : [
{
"key": 1,
"name": "UAE"
},
{
"key": 2,
"name": "India"
},
{
"key": 3,
"name": "OMAN"
}
]
you can use the below to bind your select tag to value and name
<select name="text" ng-model="name" ng-options="c.key as c.name for c in countries"></select>
it works great
<select ng-model="color" ng-options="(c.name+' '+c.shade) for c in colors"></select><br>
A year after the question, I had to find an answer for this question as non of these gave the actual answer, at least to me.
You have asked how to select the option, but nobody has said that these two things are NOT the same:
If we have an options like this:
$scope.options = [
{ label: 'one', value: 1 },
{ label: 'two', value: 2 }
];
And we try to set a default option like this:
$scope.incorrectlySelected = { label: 'two', value: 2 };
It will NOT work, but if you try to select the option like this:
$scope.correctlySelected = $scope.options[1];
It will WORK.
Even though these two objects have the same properties, AngularJS is considering them as DIFFERENT because AngularJS compares by the reference.
Take a look at the fiddle http://jsfiddle.net/qWzTb/.
The correct answer to this question has been provided by user frm.adiputra, as currently this seems to be the only way to explicitly control the value attribute of the option elements.
However, I just wanted to emphasize that "select" is not a keyword in this context, but it is just a placeholder for an expression. Please refer to the following list, for the definition of the "select" expression as well as other expressions that can be used in ng-options directive.
The use of select as it is depicted in the question:
ng-options='select p.text for p in resultOptions'
is essentially wrong.
Based on the list of expressions, it seems that trackexpr may be used to specify the value, when options are given in an array of objects, but it has been used with grouping only.
From AngularJS' documentation for ng-options:
array / object: an expression which evaluates to an array / object to
iterate over.
value: local variable which will refer to each item in
the array or each property value of object during iteration.
key: local variable which will refer to a property name in object during
iteration.
label: The result of this expression will be the label for
element. The expression will most likely refer to the value
variable (e.g. value.propertyName).
select: The result of this expression will be bound to the model of the parent element.
If not specified, select expression will default to value.
group: The result of this expression will be used to group options using the DOM
element.
trackexpr: Used when working with an array of objects. The result of this expression will be used
to identify the objects in the array. The trackexpr will most likely refer to the
value variable (e.g. value.propertyName).
Selecting an item in ng-options can be a bit tricky depending on how you set the data source.
After struggling with them for a while I ended up making a sample with most common data sources I use. You can find it here:
http://plnkr.co/edit/fGq2PM?p=preview
Now to make ng-options work, here are some things to consider:
Normally you get the options from one source and the selected value from other. For example:
states :: data for ng-options
user.state :: Option to set as selected
Based on 1, the easiest/logical thing to do is to fill the select with one source and then set the selected value trough code. Rarely would it be better to get a mixed dataset.
AngularJS allows select controls to hold more than key | label. Many online examples put objects as 'key', and if you need information from the object set it that way, otherwise use the specific property you need as key. (ID, CODE, etc.. As in the plckr sample)
The way to set the value of the dropdown/select control depends on #3,
If the dropdown key is a single property (like in all examples in the plunkr), you just set it, e.g.:
$scope.dropdownmodel = $scope.user.state;
If you set the object as key, you need to loop trough the options, even assigning the object will not set the item as selected as they will have different hashkeys, e.g.:
for (var i = 0, len = $scope.options.length; i < len; i++) {
if ($scope.options[i].id == savedValue) { // Your own property here:
console.log('Found target! ');
$scope.value = $scope.options[i];
break;
}
}
You can replace savedValue for the same property in the other object, $scope.myObject.myProperty.
For me the answer by Bruno Gomes is the best answer.
But actually, you need not worry about setting the value property of select options. AngularJS will take care of that. Let me explain in detail.
Please consider this fiddle
angular.module('mySettings', []).controller('appSettingsCtrl', function ($scope) {
$scope.timeFormatTemplates = [{
label: "Seconds",
value: 'ss'
}, {
label: "Minutes",
value: 'mm'
}, {
label: "Hours",
value: 'hh'
}];
$scope.inactivity_settings = {
status: false,
inactive_time: 60 * 5 * 3, // 15 min (default value), that is, 900 seconds
//time_format: 'ss', // Second (default value)
time_format: $scope.timeFormatTemplates[0], // Default seconds object
};
$scope.activity_settings = {
status: false,
active_time: 60 * 5 * 3, // 15 min (default value), that is, 900 seconds
//time_format: 'ss', // Second (default value)
time_format: $scope.timeFormatTemplates[0], // Default seconds object
};
$scope.changedTimeFormat = function (time_format) {
'use strict';
console.log('time changed');
console.log(time_format);
var newValue = time_format.value;
// do your update settings stuffs
}
});
As you can see in the fiddle output, whatever you choose for select box options, it is your custom value, or the 0, 1, 2 auto generated value by AngularJS, it does not matter in your output unless you are using jQuery or any other library to access the value of that select combo box options and manipulate it accordingly.
Please use track by property which differentiate values and labels in select box.
Please try
<select ng-options="obj.text for obj in array track by obj.value"></select>
which will assign labels with text and value with value(from the array)
For an object:
<select ng-model="mySelect" ng-options="key as value for (key, value) in object"></select>
It is always painful for developers to with ng-options. For example: Getting an empty/blank selected value in the select tag. Especially when dealing with JSON objects in ng-options, it becomes more tedious. Here I have done some exercises on that.
Objective: Iterate array of JSON objects through ng-option and set selected first element.
Data:
someNames = [{"id":"1", "someName":"xyz"}, {"id":"2", "someName":"abc"}]
In the select tag I had to show xyz and abc, where xyz must be selected without much effort.
HTML:
<pre class="default prettyprint prettyprinted" style=""><code>
<select class="form-control" name="test" style="width:160px" ng-options="name.someName for name in someNames" ng-model="testModel.test" ng-selected = "testModel.test = testModel.test || someNames[0]">
</select>
</code></pre>
By above code sample, you might get out of this exaggeration.
Another reference:
The tutorial ANGULAR.JS: NG-SELECT AND NG-OPTIONS helped me solve the problem:
<select id="countryId"
class="form-control"
data-ng-model="entity.countryId"
ng-options="value.dataValue as value.dataText group by value.group for value in countries"></select>
<select ng-model="output">
<option ng-repeat="(key,val) in dictionary" value="{{key}}">{{val}}</option>
</select>
Run the code snippet and see the variations. Here is note for quick understanding
Example 1(Object selection):- ng-option="os.name for os in osList track by os.id". Here track by os.id is important & should be there and os.id as should NOT have before os.name.
The ng-model="my_os" should set to an object with key as id like my_os={id: 2}.
Example 2(Value selection) :- ng-option="os.id as os.name for os in osList". Here track by os.id should NOT be there and os.id as should be there before os.name.
The ng-model="my_os" should set to a value like my_os= 2
Rest code snippet will explain.
angular.module('app', []).controller('ctrl', function($scope, $timeout){
//************ EXAMPLE 1 *******************
$scope.osList =[
{ id: 1, name :'iOS'},
{ id: 2, name :'Android'},
{ id: 3, name :'Windows'}
]
$scope.my_os = {id: 2};
//************ EXAMPLE 2 *******************
$timeout(function(){
$scope.siteList = [
{ id: 1, name: 'Google'},
{ id: 2, name: 'Yahoo'},
{ id: 3, name: 'Bing'}
];
}, 1000);
$scope.my_site = 2;
$timeout(function(){
$scope.my_site = 3;
}, 2000);
})
fieldset{
margin-bottom: 40px;
}
strong{
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.10/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<!--//************ EXAMPLE 1 *******************-->
<fieldset>
<legend>Example 1 (Set selection as <strong>object</strong>)</legend>
<select ng-model="my_os" ng-options="os.name for os in osList track by os.id">
<option value="">--Select--</option>
</select>
{{my_os}}
</fieldset>
<!--//************ EXAMPLE 2 *******************-->
<fieldset>
<legend>Example 2 (Set selection as <strong>value</strong>. Simulate ajax)</legend>
<select ng-model="my_site" ng-options="site.id as site.name for site in siteList">
<option value="">--Select--</option>
</select>
{{my_site}}
</fieldset>
</div>
Like many said before, if I have data something like this:
countries : [
{
"key": 1,
"name": "UAE"
},
{
"key": 2,
"name": "India"
},
{
"key": 3,
"name": "OMAN"
}
]
I would use it like:
<select
ng-model="selectedCountry"
ng-options="obj.name for obj in countries">
</select>
In your Controller you need to set an initial value to get rid of the first empty item:
$scope.selectedCountry = $scope.countries[0];
// You need to watch changes to get selected value
$scope.$watchCollection(function() {
return $scope.selectedCountry
}, function(newVal, oldVal) {
if (newVal === oldVal) {
console.log("nothing has changed " + $scope.selectedCountry)
}
else {
console.log('new value ' + $scope.selectedCountry)
}
}, true)
Here is how I solve this problem in a legacy application:
In HTML:
ng-options="kitType.name for kitType in vm.kitTypes track by kitType.id" ng-model="vm.itemTypeId"
In JavaScript:
vm.kitTypes = [
{"id": "1", "name": "Virtual"},
{"id": "2", "name": "Physical"},
{"id": "3", "name": "Hybrid"}
];
...
vm.itemTypeId = vm.kitTypes.filter(function(value, index, array){
return value.id === (vm.itemTypeId || 1);
})[0];
My HTML displays the option value properly.
ngOptions directive:
$scope.items = [{name: 'a', age: 20},{ name: 'b', age: 30},{ name: 'c', age: 40}];
Case-1) label for value in array:
<div>
<p>selected item is : {{selectedItem}}</p>
<p> age of selected item is : {{selectedItem.age}} </p>
<select ng-model="selectedItem" ng-options="item.name for item in items">
</select>
</div>
Output Explanation (Assume 1st item selected):
selectedItem = {name: 'a', age: 20} // [by default, selected item is equal to the value item]
selectedItem.age = 20
Case-2) select as label for value in array:
<div>
<p>selected item is : {{selectedItem}}</p>
<select ng-model="selectedItem" ng-options="item.age as item.name for item in items">
</select>
</div>
Output Explanation (Assume 1st item selected):
selectedItem = 20 // [select part is item.age]

Categories

Resources