Phil
Phil

Reputation: 4092

AngularJS scope updated after ng-change

I have a directive that centralises my select HTML and functionality but I have an issue where the ng-model is updating after ng-change happens.

Here's a focused jsfiddle example:

http://jsfiddle.net/U3pVM/1568/

(Code because SO complains otherwise)

HTML:

<div ng-app="myApp">    
    <div ng-controller="MainCtrl">
        <p>fooId is currently : {{fooId}}</p>

        <app-drop-down model="fooId" options="fooOptions" opt-value="id" opt-label="label" on-change="dropDownChanged"></app-drop-down>
    </div>
</div>

JS:

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

app.controller('MainCtrl', function ($scope, $log) {
    $scope.fooId = -1;

    $scope.fooOptions = [{
        id: 1,
        label: "A"
    }, {
        id: 2,
        label: "B"
    }];

    $scope.dropDownChanged = function (id) {
        $log.info('changed : ' + $scope.fooId + ' but really: ' + id);
    };
});

app.directive('appDropDown', function () {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            model: '=',
            options: '=',
            onChange: '='
        },
        template:
            '<div><select ng-model="model" ng-options="a[optValue] as a[optLabel] for a in options" ng-change="changed()"></select></div>',
        link: function (scope, element, attrs) {
            scope.optValue = attrs.optValue;
            scope.optLabel = attrs.optLabel;

            scope.changed = function () {
                scope.onChange(scope.model);
            };
        }
    };
});

The console logs:

changed : -1 but really: 1

changed : 1 but really: 2

When you change the select to A, then to B.

It is updating but after the ng-change is triggered.

Obviously, I can work around this by passing the id (like I do) or using $watch in the controller on the value but this isn't ideal for certain more complex scenarios.

Any ideas?

Upvotes: 2

Views: 3944

Answers (1)

HeinoVDS
HeinoVDS

Reputation: 115

I know this is a bit after the fact, but I had a similar problem and searching around I found this question as well. As there doesn't seem to be a real answer, I thought to post what I ended up doing as it may help someone else in the future. This seems to work for my case (and your fiddle as well) but as I'm only starting to use AngularJS, I might be doing something against the 'rules' so any specialist, feel free to correct me...

Anyway, here is an updated version of your Fiddle with my changes:

http://jsfiddle.net/5vb5oL7e/1/

And here is the actual code of the directive:

app.directive('appDropDown', function () {
  return {
    restrict: 'E',
    replace: true,
    scope: {
        model: '=',
        options: '=',
        onChange: '='
    },
    template:
        '<div><select ng-model="model" ng-options="a[optValue] as a[optLabel] for a in options"></select></div>',
    link: function (scope, element, attrs) {
        scope.optValue = attrs.optValue;
        scope.optLabel = attrs.optLabel;
        scope.$watch('model', function(newValue, oldValue)
        {
          // Execute function on change
          if (scope.onChange !== undefined &&
            newValue !== undefined && oldValue !== undefined)
          {
            scope.onChange();
          }
        });
    }
  };
});

Basically, what I did was to add a watch inside the link function on the model. Inside this watch I fire the onChange function when it's defined. The added checks for undefined on the old and new value were added to prevent the function to change unneeded on page load.

Hope this helps someone...

Kind regards, Heino

Upvotes: 1

Related Questions