2

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?

enter image description here

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;
        }
    }
};
PSL
  • 123,204
  • 21
  • 253
  • 243
raju
  • 4,788
  • 15
  • 64
  • 119
  • Is your model `subStatuses` bound asyncronously? – PSL Jan 12 '15 at 19:22
  • @JonathanPalumbo OP is already doing it in the ng-init. I dont hink it is a duplicate – PSL Jan 12 '15 at 19:25
  • @PSL Would the `ng-init` be invoked again after subStatuses is changed asynchonously? Without a plunker I can not be sure but I would guess the `ng-init` assignment is only ever run once, so after a parent selection is made the model of the child select is still not valid. Again I can't be sure. – Jonathan Palumbo Jan 12 '15 at 19:29
  • @JonathanPalumbo No it wont. ng-init should not be used for these. Yes it is ng-init abuse.. :) – PSL Jan 12 '15 at 19:29
  • 1
    Yes, I was guilty of abusing it until I was bitten. Although I think the symptom is a duplicate. The cause is different. – Jonathan Palumbo Jan 12 '15 at 19:35

1 Answers1

4

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.

From Doc

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>
PSL
  • 123,204
  • 21
  • 253
  • 243