Set $Scope value of a Controller from an Angular Directive

Asked

Viewed 1,117 times

2

I’m trying to make from an event in a Directive, is called a method belonging to a Controller, the Controller method call is already correct, only it seems that the $scope, within that method is wrong, for if the $scope.file.data from the call of the Directive event he up to arrow the value, but does not update the View, already if the $scope.file.data from the event of ng-click in div Controller, everything happens as expected.

What I’m trying to do is this:

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

app.controller('myController', function($scope) {
  $scope.file = {
    data: "nada!"
  };
  $scope.setExternal = function(data) {
    console.log(data);
    $scope.file.data = data;
  };
});

app.directive('myDirective', function() {
  return {
    restrict: 'A',
    scope: {
      setOnClick: '&'
    },
    link: function(scope, element, attrs) {
      element.on("click", function() {
        scope.setOnClick({
          param: "Clicou na directive!"
        });
      });
    }
  };
});
body {
  margin: 0px;
}
.box {
  padding: 25px 0px;
  border: 2px dashed #bbb;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  text-align: center;
  font: 20pt bold'Vollkorn';
  color: #bbb;
  margin: 10px 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="myController">
    {{file.data}}

    <div class="box" ng-click="setExternal('Clicou na div!')">
      Está é uma Div do controller. Clique aqui!
    </div>
    <div class="box" my-directive set-on-click="setExternal(param)">
      Está é a Directive. Clique aqui!
    </div>
  </div>
</div>

Example also in jsFiddle.

What I can is failing to do so that call from the Directive:

element.on("click", function() {
    scope.setOnClick({
        param : "Clicou na directive!"
    });
});

have a $scope valid as the ng-click of div?

Note: in my real case the event in Directive is not a simple click, a slightly more complex process. I used an example with click for didactic and practical purposes.

1 answer

1


Just add scope.$apply();:

element.on("click", function() {
    scope.setOnClick({
        param : "Clicou na directive!"
    });
    scope.$apply();
});

Fiddle updated

Tip: use $scope only for passing data between directives. At Angular 2, $scope will be completely modified as many developers use $scope for everything, and end up creating problems that would not exist if they used the controllers correctly (remember that directive's have controllers).

Another advantage is that it is much more difficult to have name conflicts and provide better code re-usability.

  • Um, so Scope, that’s right? What happens is that it’s not applying the changes in the View and the $apply() does this. Right? Interesting but strange =(. And regarding the tip: as I do not know very well angular, I did not understand if what I am doing is correct, or is wrong, starting from your tip? I think that’s right, because I’m using $scope for data passing only.

  • 1

    Yes, just missed the $apply. For some reason Angular not doing Dirty check. Using $apply, we force this. Note that Angular does not allow nested calls to $apply and that Angular itself uses $apply to perform the Dirty check. About the use of the $scope, in your guideline is correct, but in your controller, the correct one would be to use this.file = ... and this.setExternal = ..., for example. You need to change the calls in your view to make it work, and you need to give the controller a nickname (ng-controller='myController as apelido').

  • 1

    Don’t worry, the Angular doc is not very clear about this. In version 2, they are making explicit the mode to use $scope, including the possibility to cease to exist and be automatically manipulated by Angular (version 2 has not yet been fully defined its architecture, but will not be compatible with version 1 at all).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.