RastoStric
RastoStric

Reputation: 310

ng-click is faster than Angular data binding

I wanted to create an own checkbox input control. It should behave just like <input type="checkbox" ng-model="check" ng-change=”handler()”> with a different look. I created a custom directive with an isolated scope and instead of ng-model I used binding ‘=’ between the directive’s and the controllers’s scope variable and instead of ng-change I used binding ‘&’ between the directive’s on-click handler and a controller’s scope method (handler).

Controller

.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
  $scope.check = false;
  $scope.messages = [];

  $scope.onCheckChange = function(value) {
    $scope.messages.push('onCheckChange: $scope.check == ' + $scope.check + ' value == ' + value);
  }
}])

Directive

.directive('myCheckbox', function() {
  return {
    restrict: 'A',
    scope: {
        title:           '@',
        value:           '=',
        onChangeHandler: '&'
    },
    template:
      '<span class="my-checkbox"> ' +
        '<span class="tickbox" ng-click="click()" ng-class="{\'ticked\': value}" title="{{title}}"></span>' +
      '</span>',
    link: function(scope, element, attrs) {
      scope.click = function() {
        scope.value = ! scope.value;
        scope.onChangeHandler({value: scope.value});
      };
    }
  };
})

A working plunk is here http://plnkr.co/edit/oIQbqSzcWUShfdTyDBTl?p=preview.

Things work as I would expect with one exception:

  1. User click the checkbox, directive’s scope.click() is called which sets scope.value to the new checkbox value and sends this new value as a parameter to the controller’s $scope.onCheckChange()
  2. But in the controller’s method $scope.onCheckChange() the $scope.check variable does not contain the new value, but the previous one, although the parameter ‘value’ is the new value already.
  3. After ‘a while’ is even the $scope.check variable set to the new value. It seems, that the call from the on-click() is somehow ‘faster’ than the Angular data binding. I have thought about scope.$apply but don’t think this is the case - Angular takes care of the binding nicely but it takes time until it bubbles through…

Questions:

  1. Why is this happening? I would like to know it to avoid some nasty surprises, if there are some waiting for me there in the depths of the Angular see. Similar, not answered question: angular directive data binding happens after ng-change
  2. Is there a way how to make a new checkbox directive ‘listen’ to native ng-model, ng-change or ng-disable? I would like to approach custom directives the same way as the native ones. Or is it just names of the bindings?

Upvotes: 1

Views: 958

Answers (2)

Matjaz Lipus
Matjaz Lipus

Reputation: 711

Use ngModel http://plnkr.co/edit/IPWhZ8?p=preview

Your problem was because your handler was triggered before digest cycle finished updating $scope.check.

Upvotes: 0

RastoStric
RastoStric

Reputation: 310

I still don't know why is it happening, but I've found a workaround: to wrap the handler to a $timeout block without specific delay. It does not matter if it is the handler of the directive or the handler in the controller, both do the trick. The $timeout gives angular time to bubble the binding through.

Anyway, I am still curious why it is like this.

Upvotes: 0

Related Questions