Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - ngModel bound dropdown value changes visually while variable doesn't

I have a dropdown list:

<select
   ng-model="filter.country"
   ng-options="country.code as country.name for country in countries"
   ng-change="broadcast()">
   <option value="">All Countries</option>
</select>

$scope.countries is initially populated by a service, and then another dropdown change event would limit the values of $scope.countries by calling the service again, passing by the other dropdown's selected item.

The issue here is when $scope.filter.country is already bound to a value (other than the default value) and $scope.countries gets updated to a new list that doesn't include $scope.filter.country's value. I can see countries dropdown reverting back to its default option "All Countries", however $scope.filter.country remains as it was.

Any ideas about this scenario? Shouldn't $scope.filter.country get updated back to the default value?

Update: Here is a fiddle

Update:

Just to illustrate this, here is a screenshot from the fiddle: enter image description here

This does look like a bug to me, I have opened an issue for it.

Update: This has been addressed and fixed by angularjs team; demo here.

like image 370
Aziz Alfoudari Avatar asked Jun 06 '14 19:06

Aziz Alfoudari


3 Answers

The behavior you see is exactly how double-binding should work:

Most developers have a "feel" for double-binding more than an understanding. The "feeling" is that any visual change to a model-bound input results in a model change.

But any developers who have tried mode-binding a JQuery date picker will attest to this phenomenon: The JQuery Date-Picker will not update an angular model bound to same input. In other words, picking a date through JQuery does not register with any AngularJS events.

Similarly, pasting values into model-bound inputs usually does not result in the model being updated.

Analyzing your case:

You have changed the list of options for a model bound drop-down. As a result, the displayed option reverts back to default --> because the model-bound value is not a relevant choice.

Angular's ng-model doesn't care about the "options list" for your drop-down. The framework is waiting for some event that will cause it to run through the digest cycle and absorb a change of value picked. Even though something has "appeared" to change, this is an illusion or loop-hole depending on how you see it.

For a drop-down (select element), a change event is a "click" on an option.

like image 93
Dave Alperovich Avatar answered Nov 03 '22 18:11

Dave Alperovich


I had issues with that in the past. It took me a good while to fix.

Try replacing your select by:

<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange(); filter.country = undefined">
    <option value="">Change Me</option>
</select>

adding the "filter.country = undefined" to your ng-change should reset the value of your model.

Hopefully that will sove your problem.

like image 35
user3794206 Avatar answered Nov 03 '22 17:11

user3794206


If the contents of the countries select is independent of the value of changeMe, then two-way binding won't help us here. Instead, we have to make the change manually as follows:

$scope.changeMeChange = function () {

    // this or a $scope.$watch for independent values in the changeMe drop down menu
    if ($scope.filter) {
        $scope.filter.country = null;
    }

    $scope.countries = [
        {
            "code": "MX",
            "name": "Mexico"
        }
    ];
};

If the the values in the changeMe select can be used to filter the country select and thus make the country select dependent upon the change me select, then we can use a filter as follows:

'use strict';

var myApp = angular.module('myApp', []);

function FilterCtrl($scope) {
    $scope.countries = [
        {
            "code": "KW",
            "name": "Kuwait"
        },
        {
            "code": "US",
            "name": "USA"
        },
        {
            "code": "MX",
            "name": "Mexico"
        }
    ];

    $scope.dummyValues = ['MX'];


    $scope.changeMeChange = function() {
        $scope.countries = [
            {
                "code": "MX",
                "name": "Mexico"
            }
        ];
    };
};

<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange()">
    <option value="">Change Me</option>
</select>
<select ng-model="filter.country" ng-options="country.code as country.name for country in countries | filter: { code: changeMe} ">
    <option value="">All Countries</option>
</select>

Updated Fiddle using the filter approach as it seems the more likely use case. If you want your users to have access to the complete list of countries if no change me option has been selected, then a Custom Filter is the way to go.

Hope this helps.

like image 41
m.casey Avatar answered Nov 03 '22 18:11

m.casey