Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a directive talk to a controller in Angular JS?

Tags:

angularjs

I have been watching the videos by John Linquist and in one video he gives this as an example:

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

app.controller("AppCtrl", function ($scope) {
   $scope.loadMoreTweets = function () {
      alert("Loading tweets!");
   }
}

app.directive("enter", function() {
   return function (scope, element, attrs) {
      element.bind("mouseenter", function () {
         scope.LoadMoreTweets();
      })
   }
}

One thing I am wondering about is should the directive in this example talk back to the controller or would it be a better programming practice to create a service and then have the directive talk to a service? I guess I am still not sure if it is common practices for directives to talk to controllers in this way.

U tube video

like image 551
Alan2 Avatar asked Apr 05 '13 12:04

Alan2


3 Answers

How I would have done it

<span enter="loadMoreTweets()">Something</span>

JS

app.controller('AppController', function ($scope) {
   $scope.loadMoreTweets = function () {
      console.log("Loading tweets!");
   }  
})



app.directive("enter", function() {
   return {
     link: function (scope, element, attrs) {
       element.bind("mouseenter", function () {
          scope.$apply(attrs.enter)
       })
     }
   }
});

Demo: Plunker

Another way to achieve the same

app.directive("enter", function() {
   return {
    scope: {
      callback: '&enter'
    },
    link: function (scope, element, attrs) {
       element.bind("mouseenter", function () {
          scope.$apply('callback()')
       })
     }
   }
});

Demo: Plunker

like image 133
Arun P Johny Avatar answered Oct 24 '22 08:10

Arun P Johny


Is it common practice?

Yes that is common practice, directives frequently need to access the $scope for variables, and to call methods (like LoadMoreTweets).

Even in the official docs they have examples like..

scope.$watch(...)

...fom inside a directive (where the controller might be making changes to the watched variable).

It is entirely appropriate for a directive to communicate with a controller in this way.

You can even inject a controller itself (not just the scope) into the link method of a directive. Looks at the "Linking function" section of http://docs.angularjs.org/guide/directive.

"The controller is shared among all the directives, which allows the directives to use the controllers as a communication channel."

But could I use a service?

Sure you could. The question is why? If you are working with a single page (that displays tweets for example) and the controller already has a $scope.tweets variable and $scope.loadMoreTweets method, you are overcomplicating things if you try to stick a service in there where it's not needed.

On the other hand services are perfect for application wide data, like user profile. If your directive needed to access the currently logged in user, then it makes a lot of sense to do that via service.

From the docs:

Angular services are singletons that carry out specific tasks common to web apps, such as the $http service that provides low level access to the browser's XMLHttpRequest object.

If you have a application-wide task that is best suited for a singleton, by all means use a service. If you just need to access/modify the $scope from a directive, just talk directly to the controller, or trigger an event that the controller can respond to.

like image 24
jszobody Avatar answered Oct 24 '22 10:10

jszobody


Calling outer scope functions like that is risky, because it makes assumptions about the surrounding scope. What would happen if the directive were used in a different context?

There are better[1] strategies that a reusable directive may employ to communicate with the outside world:

  • Deferring its dependencies to services. This is more appropriate for system wide features. I guess it doesn't apply to your example.
  • Defining an isolate scope that contains some &attr property which is bound to an expression from an outer scope. The directive can call that expression as a method of its private scope.

As an example of this second strategy, I've modified your example. See it in action here http://plnkr.co/5uOBNu

var app = angular.module('twitterApp', []);
app.controller("AppCtrl", function ($scope) {
    $scope.loadMoreTweets = function() {
        alert("Loading tweets!");
    };
});

app.directive("specialEnter", function() {
    return {
        scope: {
            onEnter: '&'
        },
        link: function(scope, element, attrs) {
            element.bind("mouseenter", function() {
                scope.onEnter();
            });
        }
    };
});

<div ng-controller="AppCtrl">
    <div special-enter on-enter="loadMoreTweets()">Hover here!</div>
</div>

[1] "Better" in a sense of avoiding hard-wired dependencies.

like image 2
Humberto Avatar answered Oct 24 '22 08:10

Humberto