Search code examples
angularjsng-options

ngOptions: select fetched value OR select default value


After looking at the documentation and numerous questions here on SO, I can't find a solution for my issue. I have a dropdown that is populated with data from a database. To fill this, I use ngOptions with the select as method.

<select ng-model="modelthing" ng-options="item.id as item.description for item in categories">

Now what I want is a default option (which can be changed by an admin, so it can't be hardcoded as an <option>) if no value has been selected before, and the selected value when there is one of those.

$scope.categories = $http.get(thingurl)
    .then(function (result) {
        $scope.categories = result.data;
        //$scope.modelthing= $scope.categories[1].id; 
        $scope.modelthing= 1; 
    });

There are two problems with this, first of all the [1] selects the second entry in the json data, not the one that has 1 as key. And second: Solved this with help from Icycool in their comment below.

When I keep opening and closing a populated form about half of the time I get the default value, and the other half I get the actual selected value. This is obviously because the ng-model="modelthing" gets overwritten when I fetch the data form the populated form and the code runs synchronous. Is there a way to make sure this runs asynchronous? Or is my approach just plain wrong?


Other things I've tried so far:

I noticed that when I used the select as item in items phrase in ngOptions, the options in my select have a value="number:3" instead of a plain number. Using track by in ngOptions instead of select as fixed this, but had as unforseen affect that my selected values wouldn't match the ones in the dropdown, because those are plain 3 in stead of number:3.

A solution that does work, but feels very unAngular and a tad dirty, is to check on form initialization if the modelthing model is empty, and if so, set it to the default value.


Solution

  • Using id is easier to manage than using the object itself.

    <select ng-model="modelthing" 
            ng-options="item.id as item.description for item in categories">
    
    $scope.categories = $http.get(thingurl)
        .then(function (result) {
            $scope.categories = result.data;
            $scope.modelthing = 1; // your model is bound to id not the object
        });
    

    Note that integer 1 and string "1" might not be treated the same in this case, it needs to match the type returned in JSON.


    Part 2: Do you mean the model and the selections comes from different $http calls? It can be fixed by checking for existing value before applying the default value

    function getData() {
      $http.then(function(data) {
        $scope.selections = data;
        if (!$scope.selected) $scope.selected = data[0].id;
      })
    }
    
    function getSelected() {
      $http.then(function(data) {
        $scope.selected = data.id;
      })
    }
    

    By this way the final value after both async calls resolve will always be the retrieved id, regardless of the order.

    The only small problem remaining is the default value will show for a moment until the real selected value returns. You may leave this behavior or hide the selections until the selected value returns.