30

Let's say we have the following service:

myApp.factory('FooService', function () { ...

Then, from a controller, I would say:

myApp.controller('FooCtrl', ['$scope', 'FooService', function ($scope, FooService) { ...

The two-part question is:

  1. Global Accessibility: If I have 100 controllers and all need access to the service, I don't want to explicitly inject it 100 times. How can I make the service globally available? Only thing I can think of at the moment is wrapping it from within the root scope, which defeats the purpose.
  2. Accessibility from view: How can I access the service from within the view? This post suggests wrapping the service from within the controller. If I am going to that length, seems I ought to just implement the functionality right on the root scope?

3 Answers 3

41

Found a reasonable solution. Inject it into the bootstrap method (run), and add it to the root scope. From there it will be available to all controllers and views.

myApp.run(function ($rootScope, $location, $http, $timeout, FooService) {
    $rootScope.foo = FooService;
    ....

Re-reading the post I mentioned above, it didn't say "wrap" exactly... just "abstract", so I presume the poster was referring to this same solution.

For thoroughness, the answer to (1) is then:

myApp.controller('FooCtrl', ['$scope', function ($scope) { 
    // scope inherits from root scope
    $scope.foo.doSomething();
    ...

and the answer to (2) is simply:

{{doSomething()}}

Adding Christopher's comment to make sure it's seen:

@rob - According to best practices, the factory should be injected in to the controllers that need to use it, rather than on the root scope. As asked, question number one actually is the antipattern. If you need the factory 100 times, you inject it 100 times. It's barely any extra code when minified, and makes it very clear where the factory is used, and it makes it easier (and more obvious) to test those controllers with mocks, by having the required factories all listed in the function signature. – Christopher WJ Rueber Nov 25 '13 at 20:06

Sign up to request clarification or add additional context in comments.

8 Comments

A perfect answer. I wonder if any angular googlers can offer and better solution? Certainly for services such as logging and analytics this is very important.
Is there a way to amend this so that the binding is added inside the service definition?
@sidonaldson This is generally considered an anti-pattern in the angular world. Because it makes for a less testable environment (due to the assumption that the service is always available on a scope). That's why you don't see many posts on this topic.
@rob - According to best practices, the factory should be injected in to the controllers that need to use it, rather than on the root scope. As asked, question number one actually is the antipattern. If you need the factory 100 times, you inject it 100 times. It's barely any extra code when minified, and makes it very clear where the factory is used, and it makes it easier (and more obvious) to test those controllers with mocks, by having the required factories all listed in the function signature.
@chris - I agree and have since come to the same conclusion after using Angular for some time. If you articulate this in an answer I will select it at the best answer (if that's possible, haven't overridden an answer selection before). Otherwise I will probably just edit this post when I find the time.
|
6

As far as accessing the service directly in the view, that seems exceedingly un-angular. Binding it to a scope variable in the controller seems like a better solution than using the service directly in the UI to help maintain separation of duties.

6 Comments

Consider an auth service that tells you whether you are logged in, to guard a feature (ie) via ng-show. From the view you would want to say something like ng-show='authService.isLoggedIn()'
I disagree with that as being the "right" way to do it though. The more zen-angular way seems to be setting a scope variable in the controller based on the service call, and then binding to the scope variable rather than directly to the service call. Separation of duties!
How is what you are saying different from the answer above?
Functionally, nothing if that's all you're concerned about. It'll get the job done. Practically, it keeps things in the right place and improves maintainability. It's good practice and will help other future developers working on it.
I am saying literally, as in codewise what is the difference. I have a pointer to the service from within a controller in the above example. It sounds to me like that's exactly what you are saying? Not seeing the difference? Sounds like splitting a hair?
|
6

Complementing question #1 (Global accesibillity) I will only add that in order to avoid issues when minifiying the file (if that's the case) it should be written like this:

this.app.run(["$rootScope", "Foo", function($rootScope, FooService) {
    return $rootScope.fooService = FooService;
  }
]);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.