HTML select in Angular JS
HTML
select
element is often used to create a drop-down list.You could use either
ng-repeat
or ng-options
directive to fulfill it with options.
select
directive used without ngOptions
is always a string.
ng-options
directive rather than ng-repeat
one.
ng-model
value is undefined
, has a wrong type or is not contained among the options set,
than selected element into your drop-down would be empty.Note: It also might happen when
ng-model
variable is distinct into the child-scope from the one that u've defined in JS.
Should you reload your data later,
ngRepeat
will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance. If you don't have a unique identifier, track by $index
can also provide a performance boost.
ng-model="fooBar"
variable and no longer refer up to the parent’s $scope.fooBar
variable.To not storing the data directly on the scope helps in this case.
Implementation with ngRepeat
app.js
var app = angular.module('amApp', []);
app.controller('MainCtrl', function($scope, $filter) {
$scope.fruits = [
{id:1, name:'apple', price:0.65},
{id:2, name:'orange', price:0.75}
];
$scope.selectedFruitId = $scope.fruits[0].id.toString(); // 0_o
*.html
<div ng-controller="MainCtrl">
<h3>Implementation with <code>ngRepeat</code>:</h3>
<label>fruits:</label>
<select class="form-control" ng-model="selectedFruitId">
<option ng-repeat="fruit in fruits" value="{{fruit.id}}">{{fruit.name}}</option>
</select>
</div>
Empty selected option in select
element pitfalls
Wrong type
Watch-out, type-casting is required in the following line:
$scope.selectedFruitId = $scope.fruits[0].id.toString();
Because id
is integer, but string is required, if you'll write just:
$scope.selectedFruitId = $scope.fruits[0].id;
you'll have empty selected element:
<option value="? number:1 ?"></option>
Empty/Undefined value
If you will not even declare model variable,
you'll have the following empty selected element:
<option value="? undefined:undefined ?"></option>
If you will declare model variable but forget to init it (e.g. $scope.selectedFruitId = null;
)
you'll have the following empty selected element:
<option value="? object:null ?"></option>
In case with not specified string (e.g. $scope.selectedFruitId = "";
)
you'll have the following empty selected element:
<option value="? string: ?"></option>
Value is not among the options:
In case with string type (e.g. $scope.selectedFruitId = "27";
)
you'll have the following empty selected element:
<option value="? string:27 ?"></option>
Prevent empty first element in your drop-down
You need to specify default either in *.js or via ng-init
:
- In JS:
$scope.selectedFruitId = $scope.fruits[0].id.toString();
- Via
ngInit
:ng-init="selectedFruitId = fruits[0].id.toString()"
Implementation with ngOptions
In many cases,
ngRepeat
can be used on<option>
elements instead ofngOptions
to achieve a similar result. However,ngOptions
provides some benefits, such as more flexibility in how the<select>
's model is assigned via the select as part of the comprehension expression, and additionally in reducing memory and increasing speed by not creating a new scope for each repeated instance.
Other words, the advantage is, that you can simply use the whole object as a selected value:
app.js
$scope.vegetables = [
{id:3, name:'potato', price:0.25},
{id:4, name:'tomato', price:0.5}
];
$scope.selectedVegetable = $scope.vegetables[0]; // ^_^
*.html
<div ng-controller="MainCtrl">
<h3>Implementation with <code>ngOptions</code>:</h3>
<label>vegetables:</label>
<select class="form-control" ng-model="selectedVegetable" ng-options="vegetable as vegetable.name for vegetable in vegetables track by vegetable.id"></select>
</div>
Note: selectedVegetable
is an object, but options are still tracked by id
:
(
track by vegetable.id
is basically the same asvalue="{{vegetable.id}}"
in our implementation withngRepeat
)
Understanding Scopes (Prototypical inheritance)
In most cases, directives and scopes interact but do not create new instances of scope. However, some directives, such as
ng-controller
andng-repeat
, create new child scopes and attach the child scope to the corresponding DOM element.
Scopes can be nested to limit access to the properties of application components while providing access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes". A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope" does not.
When new scopes are created, they are added as children of their parent scope. When Angular evaluates
{{name}}
, it first looks at the scope associated with the given element for the name property. If no such property is found, it searches the parent scope and so on until the root scope is reached. In JavaScript this behavior is known as prototypical inheritance, and child scopes prototypically inherit from their parents.
Empty selected option in select
element because of wrong Scope
According to (Prototypical inheritance) your directive might create a child-scope,
which could distinct your ng-model="fooBar"
variable and no longer refer up to the parent’s $scope.fooBar
variable.
Consequences are, that your $scope.fooBar
variable remains the same regardless of the selected option,
but relevant fooBar
variable exist in the child-scope.
So, instead of this:
// Controller
$scope.username= "Joe"
// HTML
<input ng-model="username">
Do this:
// Controller
$scope.user = {name: "Joe"};
// HTML
<input ng-model="user.name">
Now when ng-model
wants to write, it firstly reads, then writes.
It reads the user property from the element scope, and it works because a child-scope will search upwards for a read.
Once it finds user in parent-scope, it successfully updates the user.name
.
Handle the select
element option change with ngChange
directive
Below is an implementation of two related drop-downs.
The second drop-down shows either fruits or vegetables list depending of the specified category into the first drop-down:
app.js
...
$scope.groups = [$scope.fruits, $scope.vegetables];
$scope.categories = [
{id:5, name:'fruits', groupId:0},
{id:7, name:'vegetables', groupId:1}
];
$scope.selectedCategory = $scope.categories[0];
$scope.onCategoryChange = function(selectedItem) {
$log.log(selectedItem);
$log.log($scope.selectedCategory === selectedItem);
$scope.products = $scope.groups[selectedItem.groupId];
$scope.selectedProduct = $scope.products[0];
}
$scope.onCategoryChange($scope.selectedCategory);
$scope.getFullDescription = function(item) {
return item.name + ': ' + $filter('currency')(item.price); // bonus:)
};
*.html
<div ng-controller="MainCtrl">
<h3>Implementation with Change handling:</h3>
<label>categories:</label>
<select class="form-control" ng-model="selectedCategory" ng-change=onCategoryChange(selectedCategory); ng-options="category as category.name for category in categories track by category.id">
</select>
<label>products:</label>
<select class="form-control" ng-model="selectedProduct" ng-options="product as getFullDescription(product) for product in products track by product.id"></select>
</div>
Watchout: In case of distinct ng-model
variable issue, even if u'll try to set selected item manually on change:
$scope.onCategoryChange = function(selectedItem) {
$scope.selectedCategory = selectedItem;
}
you might have an extra empty selected option in to your <select>
element, which will shadow the actual problem reason.
Hope it was deep enough to help U to solve your problem, with which I've faced couple of times and dig among a lot of resources to solve it.
see Also
- Angular Recipe: Passing data into your Directive
- Angular Recipe:Insert html string from js into html (solve Error: [$sce:unsafe])
- Keynote: AngularJS Filters
- Keynote: AngularJS Services
- Keynote: AngularJS Routing
- Keynote: AngularJS Do the custom things
- Keynote: AngularJS Do the regular things
- Keynote: AngularJS Basics
- AngularJS - Docs ngRepeat
- AngularJS - Docs Scopes
- AngularJS - API select
- Github - AngularJS Scopes
- Keynote: Top JS frmeworks 2015
Thank you so much for posting keep sharing
ReplyDeleteAngular course
Angular training
angular certification
angularjs online training
angularjs online course
Angular Online Training
Angularjs Online Training Hyderabad
Angularjs Online Training India
Thank you for your very helpful post! Help me through the select being blank issue that I struggled with for quite sometime. Keep it up!
ReplyDeleteI really like and appreciate your post. Really thank you! Fantastic.
ReplyDeletepython training
angular js training
selenium trainings
Great post.I'm glad to see people are still interested of Article.Thank you for an interesting read........
ReplyDeleteOffshore Angularjs 2 Developer in India
Great blog the content is informative and engaging. Visit my website to get best Information
ReplyDeleteFrozen Shoulder Treatment in Surrey
Wonderful blog with great piece of information. Visit my website to get best Information
ReplyDeletevintage oval mirror