I have a cascading select with 2nd dropdown appears based on the first dropdown selection. An extra blank option appears which is not part of intended behaviour of the dropdown.
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name"
data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
How do I avoid empty extra select from appearing in dropdown?
code for my cascading dropdowns is
<div class="form-group" ng-class="{ 'has-error' : submitted && orderForm.content.$invalid}">
<div class="controls">
<select ng-change="getSubStatuses(order.attempt_status)" data-ng-model="order.attempt_status" ng-options="status.name as status.name for status in statuses">
</select>
</div>
</div>
<div class="form-group" ng-show="subStatuses.length" ng-class="{ 'has-error' : submitted && orderForm.content.$invalid}">
<div class="controls">
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name" data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as subStatus.name for subStatus in subStatuses">
</select>
</div>
</div>
$scope.getSubStatuses = function(attempt_status) {
var statuses = $scope.statuses;
for(var index in statuses) {
if(statuses[index].name === attempt_status) {
if(statuses[index].children) {
$scope.subStatuses = statuses[index].children;
} else {
$scope.subStatuses = [];
}
break;
}
}
};
I am assuming you are setting this for an asynchronously bound data. ng-init
is really bad for this purpose. Thumb rule is do not use ng-init
for something which controller is supposed to do. You should set up your view model in the controller. The reason why it could fail when the data is bound asynchronously is because ng-init'ed expression is not watched. So it just runs once during the first rendering and the subStatuses
is not yet populated and it falls back to showing the default empty option. And even if the data is populated and view is updated during the later digest cycle ng-init expression does not run again.
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
So just initialize it in your controller, instead of using ng-init.
$scope.getSubStatuses = function(attempt_status) {
var statuses = $scope.statuses;
for(var index in statuses) {
if(statuses[index].name === attempt_status) {
if(statuses[index].children) {
$scope.subStatuses = statuses[index].children;
} else {
$scope.subStatuses = [];
}
break;
}
}
//Initialize the model here, assuming $scope.order is already initialized
$scope.order.attempt_status_sub = ($scope.subStatuses[0] ||{}).name;
};
angular.module('app', []).controller('ctrl', function($scope, $timeout) {
$timeout(function() {
$scope.subStatuses = [{
name: 'test1'
}, {
name: 'test2'
}, {
name: 'test3'
}];
$scope.order = {
attempt_status_sub1: $scope.subStatuses[0].name
};
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
With Ng-Init
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name" data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
With controller initialization.
<select data-ng-model="order.attempt_status_sub1" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
</div>