148

I would like to add some utility functions to my AngularJS application. For example:

$scope.isNotString = function (str) {
    return (typeof str !== "string");
}

Is the best way to do this to add them as a service? From what I have read I can do this but then I would like to use these in my HTML pages so is it still possible if they are in a service? For example can I use the following:

 <button data-ng-click="doSomething()"
         data-ng-disabled="isNotString(abc)">Do Something
 </button>

Can someone give me an example of how I could add these. Should I create a service or is there some other way of doing it. Most important I would like these utility functions in a file and not combined with another part of the main set up.

I understand there's a few solutions but none of them are so clear.

Solution 1 - Proposed by Urban

$scope.doSomething = ServiceName.functionName;

The problem here is I have 20 functions and ten controllers. If I did this it would mean adding a lot of code to each controller.

Solution 2 - Proposed by me

    var factory = {

        Setup: function ($scope) {

            $scope.isNotString = function (str) {
                return (typeof str !== "string");
            }

The disadvantage of this is that at the start of every controller I would have one or more of these Setup calls to each service which passed the $scope.

Solution 3 - Proposed by Urban

The solution proposed by urban of creating a generic service looks good. Here's my main set up:

var app = angular
    .module('app', ['ngAnimate', 'ui.router', 'admin', 'home', 'questions', 'ngResource', 'LocalStorageModule'])
    .config(['$locationProvider', '$sceProvider', '$stateProvider',
        function ($locationProvider, $sceProvider, $stateProvider) {

            $sceProvider.enabled(false);
            $locationProvider.html5Mode(true);

Should I add the generic service to this and how could I do it ?

Alan2
  • 23,493
  • 79
  • 256
  • 450

7 Answers7

109

EDIT 7/1/15:

I wrote this answer a pretty long time ago and haven't been keeping up a lot with angular for a while, but it seems as though this answer is still relatively popular, so I wanted to point out that a couple of the point @nicolas makes below are good. For one, injecting $rootScope and attaching the helpers there will keep you from having to add them for every controller. Also - I agree that if what you're adding should be thought of as Angular services OR filters, they should be adopted into the code in that manner.

Also, as of the current version 1.4.2, Angular exposes a "Provider" API, which is allowed to be injected into config blocks. See these resources for more:

https://docs.angularjs.org/guide/module#module-loading-dependencies

AngularJS dependency injection of value inside of module.config

I don't think I'm going to update the actual code blocks below, because I'm not really actively using Angular these days and I don't really want to hazard a new answer without feeling comfortable that it's actually conforming to new best practices. If someone else feels up to it, by all means go for it.

EDIT 2/3/14:

After thinking about this and reading some of the other answers, I actually think I prefer a variation of the method brought up by @Brent Washburne and @Amogh Talpallikar. Especially if you're looking for utilities like isNotString() or similar. One of the clear advantages here is that you can re-use them outside of your angular code and you can use them inside of your config function (which you can't do with services).

That being said, if you're looking for a generic way to re-use what should properly be services, the old answer I think is still a good one.

What I would do now is:

app.js:

var MyNamespace = MyNamespace || {};

 MyNamespace.helpers = {
   isNotString: function(str) {
     return (typeof str !== "string");
   }
 };

 angular.module('app', ['app.controllers', 'app.services']).                             
   config(['$routeProvider', function($routeProvider) {
     // Routing stuff here...
   }]);

controller.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', function($scope) {
    $scope.helpers = MyNamespace.helpers;
  });

Then in your partial you can use:

<button data-ng-click="console.log(helpers.isNotString('this is a string'))">Log String Test</button>

Old answer below:

It might be best to include them as a service. If you're going to re-use them across multiple controllers, including them as a service will keep you from having to repeat code.

If you'd like to use the service functions in your html partial, then you should add them to that controller's scope:

$scope.doSomething = ServiceName.functionName;

Then in your partial you can use:

<button data-ng-click="doSomething()">Do Something</button>

Here's a way you might keep this all organized and free from too much hassle:

Separate your controller, service and routing code/config into three files: controllers.js, services.js, and app.js. The top layer module is "app", which has app.controllers and app.services as dependencies. Then app.controllers and app.services can be declared as modules in their own files. This organizational structure is just taken from Angular Seed:

app.js:

 angular.module('app', ['app.controllers', 'app.services']).                             
   config(['$routeProvider', function($routeProvider) {
     // Routing stuff here...
   }]);  

services.js:

 /* Generic Services */                                                                                                                                                                                                    
 angular.module('app.services', [])                                                                                                                                                                        
   .factory("genericServices", function() {                                                                                                                                                   
     return {                                                                                                                                                                                                              
       doSomething: function() {   
         //Do something here
       },
       doSomethingElse: function() {
         //Do something else here
       }
    });

controller.js:

angular.module('app.controllers', []).                                                                                                                                                                                  
  controller('firstCtrl', ['$scope', 'genericServices', function($scope, genericServices) {
    $scope.genericServices = genericServices;
  });

Then in your partial you can use:

<button data-ng-click="genericServices.doSomething()">Do Something</button>
<button data-ng-click="genericServices.doSomethingElse()">Do Something Else</button>

That way you only add one line of code to each controller and are able to access any of the services functions wherever that scope is accessible.

Community
  • 1
  • 1
urban_raccoons
  • 3,499
  • 1
  • 22
  • 33
  • I have maybe twenty of these functions and I want to use them in multiple controllers. I thought about this but it's not so practical to have the code like: $scope.doSomething = ServiceName.functionName; inside each controller. I will update my question with a bit more details. thanks – Alan2 Oct 27 '13 at 04:23
  • yeah that makes sense if you need to add a line for each function in the services, but if you can add the whole service (with all of it's functions) to the scope in one line, I think it makes sense. I'm not too clear on how the solution 2 you mentioned might work? – urban_raccoons Oct 27 '13 at 04:37
  • The second solution requires the following call in my controller: utilityService.setup($scope); Once I make this call then everything is available within the controller scope. I like your updated solution as everything is clear and it's even obvious in the HTML where that function came from. – Alan2 Oct 27 '13 at 04:45
  • Yeah I understand your solution now. – urban_raccoons Oct 27 '13 at 04:47
  • I think your solution is the way I would like to go. Can you just explain how would I add the app.services module to my application? – Alan2 Oct 27 '13 at 04:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/40060/discussion-between-urban-racoons-and-alan) – urban_raccoons Oct 27 '13 at 05:10
  • 1
    @urban_racoons: I also started this way, but unfotunately you can't inject such services in config. I wanted to access my auth_service inside an interceptor to add token to header but then I realized the service can't be injected in config. only constants can. I think adding functions to constants should be a better approach. – Amogh Talpallikar Dec 24 '13 at 10:53
  • @AmoghTalpallikar yeah I agree with you - I think it's possible that certain utilities might be better placed in a constant. The only thing I would suggest is that you put the constant within a namespace to prevent accidental collisions. – urban_raccoons Dec 26 '13 at 19:09
  • 1
    I'm just asking, because I'm not primarily a JS guy, but would using your own namespace produce a copy or a singleton? If you have a ton of modules, it seems like a waste of memory to have copies of the same service, especially if you only want to use one helper. – Eric Keyte May 12 '15 at 20:12
  • 3
    @EricKeyte The namespace is an object literal, which is a kind of singleton in that is pretty common in JS. Sorry for the delayed reply :) – urban_raccoons Jul 02 '15 at 18:26
34

Coming on this old thread i wanted to stress that

1°) utility functions may (should?) be added to the rootscope via module.run. There is no need to instanciate a specific root level controller for this purpose.

angular.module('myApp').run(function($rootScope){
  $rootScope.isNotString = function(str) {
   return (typeof str !== "string");
  }
});

2°) If you organize your code into separate modules you should use angular services or factory and then inject them into the function passed to the run block, as follow:

angular.module('myApp').factory('myHelperMethods', function(){
  return {
    isNotString: function(str) {
      return (typeof str !== 'string');
    }
  }
});

angular.module('myApp').run(function($rootScope, myHelperMethods){ 
  $rootScope.helpers = myHelperMethods;
});

3°) My understanding is that in views, for most of the cases you need these helper functions to apply some kind of formatting to strings you display. What you need in this last case is to use angular filters

And if you have structured some low level helper methods into angular services or factory, just inject them within your filter constructor :

angular.module('myApp').filter('myFilter', function(myHelperMethods){ 
  return function(aString){
    if (myHelperMethods.isNotString(aString)){
      return 
    }
    else{
      // something else 
    }
  }
);

And in your view :

{{ aString | myFilter }}   
Yashprit
  • 512
  • 1
  • 8
  • 22
nicolas
  • 900
  • 6
  • 16
  • Both solutions concern `run`-time. What about config time ? Don't we need utilities out there ? – Cyril CHAPON Sep 08 '15 at 13:25
  • config time you need a provider (a kind of service) checkout the angular js doc – nicolas Nov 27 '15 at 22:28
  • 1
    The #3 solution seems like the best to me. Once a filter is registered, you can use it anywhere else. I used it for my currency formatting & date formatting. – Olantobi Dec 19 '16 at 08:34
6

Do I understand correctly that you just want to define some utility methods and make them available in templates?

You don't have to add them to every controller. Just define a single controller for all the utility methods and attach that controller to <html> or <body> (using the ngController directive). Any other controllers you attach anywhere under <html> (meaning anywhere, period) or <body> (anywhere but <head>) will inherit that $scope and will have access to those methods.

Willis Blackburn
  • 8,068
  • 19
  • 36
  • 1
    this is definitely the best way to do this. Just have a utility controller and put it in the wrapper/container div of the whole project, all controllers within will inherit: `
    ` then in any interior views: `
    – Ian J Miller Mar 06 '17 at 15:02
4

The easiest way to add utility functions is to leave them at the global level:

function myUtilityFunction(x) { return "do something with "+x; }

Then, the simplest way to add a utility function (to a controller) is to assign it to $scope, like this:

$scope.doSomething = myUtilityFunction;

Then you can call it like this:

{{ doSomething(x) }}

or like this:

ng-click="doSomething(x)"

EDIT:

The original question is if the best way to add a utility function is through a service. I say no, if the function is simple enough (like the isNotString() example provided by the OP).

The benefit of writing a service is to replace it with another (via injection) for the purpose of testing. Taken to an extreme, do you need to inject every single utility function into your controller?

The documentation says to simply define behavior in the controller (like $scope.double): http://docs.angularjs.org/guide/controller

Brent Washburne
  • 12,904
  • 4
  • 60
  • 82
  • Having utility functions as a service allows you to selectively access them in your controllers.. if no controllers use them, then the service won't be instantiated. – StuR Feb 28 '14 at 14:50
  • I actually kind of like your approach and incorporated it into my edited answer. I have a feeling someone might have downvoted you because of the global function (and namespace pollution), but I have a feeling that you would have likely incorporated an approach similar to the one I wrote out if you thought that much hand-holding was necessary. – urban_raccoons Mar 02 '14 at 23:25
  • Personally, I can't see a problem with making generic, small, utility functions global. It would typically be things that you use all over your codebase, so anyone would become familiar with them quite quickly. See them as small extensions to the language. – Cornel Masson Apr 14 '14 at 10:26
  • In your edit you mention "The documentation says to simply define behavior in the controller (like $scope.double)". Are you saying that the documentation suggests putting utility functions in controllers? – losmescaleros Jul 20 '17 at 23:35
  • @losmescaleros Yes, read the section "Adding Behavior to a Scope Object" in the documentation http://docs.angularjs.org/guide/controller – Brent Washburne Jul 21 '17 at 21:37
4

Here is a simple, compact and easy to understand method I use.
First, add a service in your js.

app.factory('Helpers', [ function() {
      // Helper service body

        var o = {
        Helpers: []

        };

        // Dummy function with parameter being passed
        o.getFooBar = function(para) {

            var valueIneed = para + " " + "World!";

            return valueIneed;

          };

        // Other helper functions can be added here ...

        // And we return the helper object ...
        return o;

    }]);

Then, in your controller, inject your helper object and use any available function with something like the following:

app.controller('MainCtrl', [

'$scope',
'Helpers',

function($scope, Helpers){

    $scope.sayIt = Helpers.getFooBar("Hello");
    console.log($scope.sayIt);

}]);
  • 2
    This clearly shows one issue why I sometimes don't like Angular: saying "add a service"... and then in the code creating a new factory(). From design patterns, they are not the same things - factory is usually used for producing new objects, and service is, well, for "serving" some functionality or resource. In such moments I want to say "WT*, Angular". – JustAMartin Sep 22 '17 at 14:01
1

Why not use controller inheritance, all methods/properties defined in scope of HeaderCtrl are accessible in the controller inside ng-view. $scope.servHelper is accessible in all your controllers.

    angular.module('fnetApp').controller('HeaderCtrl', function ($scope, MyHelperService) {
      $scope.servHelper = MyHelperService;
    });


<div ng-controller="HeaderCtrl">
  <div ng-view=""></div>
</div>
Kie
  • 11
  • 1
  • 3
1

You can also use the constant service as such. Defining the function outside of the constant call allows it to be recursive as well.

function doSomething( a, b ) {
    return a + b;
};

angular.module('moduleName',[])
    // Define
    .constant('$doSomething', doSomething)
    // Usage
    .controller( 'SomeController', function( $doSomething ) {
        $scope.added = $doSomething( 100, 200 );
    })
;
tastybytes
  • 1,309
  • 7
  • 17