Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS Custom Directive Two Way Binding

If I have an AngularJS directive without a template and I want it to set a property on the current scope, what is the best way to do it?

For example, a directive that counts button clicks:

<button twoway="counter">Click Me</button>
<p>Click Count: {{ counter }}</p>

With a directive that assigns the click count to the expression in the two way attribute:

.directive('twoway', [
'$parse',
  function($parse) {
    return {
      scope: false,
      link: function(scope, elem, attrs) {
        elem.on('click', function() {
          var current = scope.$eval(attrs.twoway) || 0;
          $parse(attrs.twoway).assign(scope, ++current);
          scope.$apply();
        });
      }
    };
  }
])

Is there a better way to do this? From what I've read, an isolated scope would be overkill, but do I need a child scope? And is there a cleaner way to write back to a scope variable defined in the directive attribute other than using $parse. I just feel like I'm making this too difficult.

Full Plunker here.

like image 457
David Faivre Avatar asked Sep 18 '13 21:09

David Faivre


2 Answers

Why is an isolate scope overkill? its pretty useful for exactly this kind of thing:

  scope: {
     "twoway": "=" // two way binding
  },

This is a pretty idiomatic angular solution to this problem, so this is what I'd stick with.

like image 129
Doug T. Avatar answered Sep 24 '22 05:09

Doug T.


I'm surprised no one has mentioned ng-model, the default directive for doing two-data binding. Maybe it's not so well known, but the linking function has a fourth parameter:

angular.module('directive-binding', [])
  .directive('twoway', 
      function() {
        return {
          require: '?ngModel',
          link: function(scope, elem, attrs, ngModel) {
            elem.on('click', function() {
              var counter = ngModel.$viewValue ? ngModel.$viewValue : 0
              ngModel.$setViewValue(++counter);
              scope.$apply();
            });
          }
        };
      }
    );

On your view

<button twoway ng-model="counter">Click Me</button>
<p>Click Count: {{ counter }}</p>

The fourth parameter is an API for the ngModelController, which has many uses for handling (parsing and formatting, for instance) and sharing data between a directive and a scope.

Here's the updated Plunker.

like image 33
jjperezaguinaga Avatar answered Sep 22 '22 05:09

jjperezaguinaga