Angular ng-options directive ends with multiple selection

In AngularJS page, I have a dropdown that lists buildings (objects):

<select ng-model="selectedFacility"
   ng-options="facility.abbr for facility in facilities track by facility.id">
</select>
<label>{{selectedFacility.name}}</label>

      

Seems to work. The value in the dropdown shows which user selects (abbreviation) and the text on the label shows the corresponding long name. Good.

However, elsewhere I pass the call $scope.selectedFacility.id

to to $http

and I noticed that it is always 1, no matter which building is actually selected. I looked deeper (in Chrome F12) and I see that the elements all that I have selected at some point have an attribute selected="selected"

:

<option value="1" label="Engineering" selected="selected">Engineering</option>
<option value="2" label="Biology" selected="selected">Biology</option>    
<option value="3" label="CompSci">CompSci</option>    
<option value="4" label="Admissions" selected="selected">Admissions</option>    
<option value="5" label="Security">Security</option>

      

As you can see from the output above, at some point I selected items 1, 2, and 4. I am assuming that since the first item was initially selected, it always has a "selected" attribute and therefore selectedFacility.id=1

. But how do I tell Angular to remove the selected attribute? This is a regular dropdown menu, with no option to select multiple items, but multiple items will be marked as selected

UPDATE . It looks like the controller has the following:

$scope.selectedFacility= {};
$scope.facilities = facility.getFacilities().query(function(facilities) {
  $scope.selectedFacility=facilities[0];
});

      

and the tool is a provider service that retrieves the object information from the server:

  service.getFacilities = function() {
    return $resource(url);
  };

      

If I start to use $scope.selectedFacility.imeanit=facilities[0];

and selectedFacility.imeanit

in the ng-model, it will work as expected

+3


source to share


1 answer


Problem

I was able to recreate your problem here .

I set the clock to $scope.selectedFacility

to see when the model is actually updated.

$scope.$watch('selectedFacility', function(newVal, oldVal) {
  console.log(newVal);
  console.log(oldVal);
});

      

The clock is called once at the beginning:

[Log] null (main.js, line 17)
[Log] null (main.js, line 18)

      

Then again inside the callback query()

:

[Log] Resource (main.js, line 17)
    abbr: "Eng"
    id: 1
    name: "Engineering"
    __proto__: Resource
[Log] null (main.js, line 18)

      

But never again, even if the selected option is changed in <select>

.



The reason why has to do with how JS prototypal inheritance works, and the implications of that with 2-way bindings and Angular's ability to do dirty checking when child scopes are involved (which is here, thanks ng-options

). This YouTube video explains it well.

Correction

If instead of facilities

making an object whose members are list Resource

and selected Resource

then it will work. Here's another plunker showing a working example (relevant details below):

JS:

$scope.facilities = {
  selected: null,
  all: []
};

$scope.facilities.all = Facilities.query(function(list) {
  $scope.facilities.selected = list[0];
});

      

HTML:

<select ng-if="facilities.all.length"
        ng-model="facilities.selected"
        ng-options="facility.abbr for facility in facilities.all track by facility.id">
</select>
<label ng-if="facilities.selected">{{facilities.selected.name}}</label>

      

(strictly speaking, only ng-model

(i.e. facilities.selected

) should be property-of-the-property-of- $scope

, but I thought the above structure was cleaner).

+1


source







All Articles