Morphilos
Morphilos

Reputation: 157

AngularJS - Updating controller data (this.model syntax) from a directive through a service

I am trying to update a data stored in a controller (this.isUploaded) from a directive (I want to bind the update to an action on a DOM element (in this example, on a click, in my application, on a file upload through drag drop)). Based on what I read, the best way to do it is to inject a service which would act as a brdige between the two parts of the application with a data binding. The whole code I use is the following (inspired from this post, which do not work with directives) :

<body>
  <div ng-controller="navController as navCtrl">
    <p>isUploaded: {{navCtrl.isUploaded}}</p>
    <div test-view></div>
  </div>
</body>

And for the JavaScript:

angular.module('testApp', [])
  .directive('testView', ['CreateBindingService', function(createBindingService) {
    return {
      restrict: 'EA',
      template: '<a href="#">Incrementation</a>',
      link: function(scope, element, attrs) {
        element.bind('click', function(e) {
          createBindingService.incrementIsUploaded();
          console.log(createBindingService.getIsUploaded());
        });
      }
    };
  }])
  .factory('CreateBindingService', function() {
    var isUploaded = 1;
    return {
      getIsUploaded: function() {
        return isUploaded;
      },
      incrementIsUploaded: function() {
        isUploaded++;
        return isUploaded;
      }
    };
  })
  .controller('navController', ['CreateBindingService', function(createBindingService) {
    this.isUploaded = createBindingService.getIsUploaded();
  }]);

The log does behave as expected (each click on the increment changes the value stored in the service), but the value in the model is not updated.

I tried to have a look at $watch, but so far, nothing really happens, so it seems to be more of a binding problem than an update one.

Could it be because of my use of this.isUploaded instead of $scope.isUploaded? Indeed, most of the posts I've seen uses of the latter, while I use the former (I learnt the basis of Angular on Code School, where they don't use $scope).

I do know that there are simpler ways to do it (in this context, it's obious, and even my "fancy" drag and drop file upload thing could probably be replaced by a way simpler directive. However, I still think that getting an answer to my question would help me to better understand how Angular works.

But if, you suggest a fundamentally different way to tackle the problem, I'm interested :)

Upvotes: 0

Views: 258

Answers (1)

sylwester
sylwester

Reputation: 16498

It seems that bind event ours outside Angular scope so you can use it scope.$apply inside your directive. And change isUploaded to complex type please see demo below.

angular.module('testApp', [])
  .directive('testView', ['CreateBindingService',
    function(createBindingService) {
      return {
        restrict: 'EA',
        template: '<a href="#">Incrementation</a>',
        link: function(scope, element, attrs) {
          element.bind('click', function(e) {
            scope.$apply(function() {
              createBindingService.incrementIsUploaded();
              console.log(createBindingService.getIsUploaded());
            });
          });
        }
      };
    }
  ])
  .factory('CreateBindingService', function() {
    var data = {
      isUploaded: 1
    };
    return {
      getIsUploaded: function() {
        return data.isUploaded;
      },
      incrementIsUploaded: function() {
        data.isUploaded++;
        return data;
      },
      data: data
    };
  })
  .controller('navController', ['CreateBindingService',
    function(createBindingService) {
      this.data = createBindingService.data;
    }
  ]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<body ng-app="testApp">
  <div ng-controller="navController as navCtrl">
    <p>isUploaded: {{navCtrl.data.isUploaded}}</p>
    <div test-view></div>
  </div>
</body>

Upvotes: 1

Related Questions