17

I'm new to AngularJS and i see this syntax a lot:

function someFunc(){
   return function(input){
    return 'hello' + input;
  }
}

The function above is a general syntax i tend to see a lot but problem is specific with this example for custom filter:

angular.module('bookFilters', [])
    .filter('newBookFilter', function(){
          return function(input){
        return 'The Book: ' + input.name + 'is new !';
   };
});

I understand that wrapping the function with another function gives me an opportunity to use dependency injection, Here is my questions about it:

Does the filter get the function returned from the wrapping function? Then is it able to use dependency injection to inject the value into the function? Theoretically that:

This code:

{{bookObj | newBookFilter}}

Will become:

{{   bookObj | function(input){return 'The Book: ' + input.name + 'is new !'; }  }}

And finally the {{}} will return the final value from the function.

Why can't i just inject the input to the first function like:

angular.module('bookFilters', [])
         .filter('newBookFilter', function(input){
             return 'The Book: ' + input.name + 'is new !';
     });

Why dependency injection will only work on returned function?

I know i really confused here, If anyone can help me i will be very thankful, Thank you all and have a nice day.

biofractal
  • 18,963
  • 12
  • 70
  • 116
Aviel Fedida
  • 4,004
  • 9
  • 54
  • 88
  • we use the same function (.filter) with the same parameters => there should be only one implementation. How can the angular framework differentiate between your first case and your second case? – Khanh TO Jan 11 '14 at 09:39
  • if there were something like that, there would be 2 different functions similar to what we have like `.factory` and `.service` – Khanh TO Jan 11 '14 at 09:41
  • Can you help me understand why angularjs require 2 functions to be able to use dependency injection? – Aviel Fedida Jan 11 '14 at 09:49
  • no, it's not that angular js requires 2 functions. I mean if there were multiple ways to declare a service (to be injected to others), there must be different functions, each for one implementation. – Khanh TO Jan 11 '14 at 09:52

3 Answers3

22

The answer is opposite to your question. Angular injects only into factory function but not into resulting function:

   .filter('newBookFilter', function($log, ...){ // <- factory function
       return function(input, ...){              // <- resulting function
       };
   })

Factory function have arbitrary injected parameters. Resulting function have fixed parameters.

Second reason is that you can do some initialization in factory function. This is useful for example when you define new directive. Also factory returns closure which can capture variables and arguments from factory function. See example below. It uses dependency injection to get logging object. Here is full example.

  .filter('joinBy', function ($log) {     // <- injected here
    return function (input, delimiter) {  // <- used here
      var res = (input || []).join(delimiter || ',');
      $log.info(res);
      return res;
    };
  });
Vasiliy Kevroletin
  • 1,188
  • 13
  • 17
  • Hey vassiliy thank you for your answer can you explain: "Factory function have arbitrary injected parameters. Resulting function have fixed parameters.".... – Aviel Fedida Jan 11 '14 at 10:32
  • _I marked which function is factory and which is resulting(see first code block)_. By "arbitrary injected parameters" I mean that angular will pass "required" parameters inside this function. Angular sees function arguments names to understand that function want to "eat" as a parameter. – Vasiliy Kevroletin Jan 11 '14 at 10:41
  • By "fixed parameters" I mean that angular always sends same set of parameters and doesn't see to arguments names. Good example is "link" function in definition of new directive. It always have 4 parameters _(last is optional)_. I don't 100% sure about result of filter function. I will search for documents. – Vasiliy Kevroletin Jan 11 '14 at 10:49
  • [Here](http://docs.angularjs.org/api/ng.filter:filter) is description of arguments for resulting function. [Here](http://docs.angularjs.org/api/ng.$filterProvider) is answer to initial question "filter definition consists of a factory function which is annotated with dependencies and is responsible for creating a filter function" _(filter function is **not** annotated with dependencies)_ – Vasiliy Kevroletin Jan 11 '14 at 11:01
  • If you are minifying this, it won't work. The minification will eat the $log and then angular won't be able to resolve it. Do you know how to define this with the string notation like you do controllers etc? – Bill Leeper Jun 29 '15 at 22:44
  • [Similar to controllers](http://plnkr.co/edit/AbZX0dgSNnJ834e0Ix6o) (pay attention to script.js file) – Vasiliy Kevroletin Jun 30 '15 at 12:00
11

I think of Angular factory, service, filter, directive wrappers as ovens that create JavaScript objects and functions with Angular flavors. So, to borrow the same style from Vasiliy's answer:

// Don't use this code in a real app. It's just to illustrate a point.
angular.module('App', [])

// The following oven makes an Angular flavored JavaScript function that 
// formats a currency
.service('currencyBadFilterFn',
  // We inject a built-in Angular filter, currencyFilter, into our oven
  function(currencyFilter) { 
    // oven produces a function that can be used in other places in Angular code
    return function(number) {
      // produced function returns a currency-formatted number when used
      return currencyFilter(number)   
    }
  }
)

.controller('MainCtrl',
  function($scope, currencyBadFilterFn) {
    $scope.amount = currencyBadFilterFn(10) // $10.00
  }
)

As you can see, the same pattern is used in creating services. Here, we are creating a service that returns a function that we can use in other places in our code.

The first function, the oven function, along with the .service or .factory or .filter wrapper, tells Angular how to build your function. The return value of that first function is what you will use in your code.

M.K. Safi
  • 6,560
  • 10
  • 40
  • 58
  • So as i understand i can use dependency injection only for the wrapper function, The all idea is like factory which instantiate ones the the value used multiple times? – Aviel Fedida Jan 11 '14 at 11:37
  • @uBlankText yes! The value that's returned by these wrappers is a singleton that can be used in other places in your code. – M.K. Safi Jan 11 '14 at 11:39
  • So the reason(besides the singleton advantage) for using 2 functions is that 1 (wrapper function) will take dependencies and the returned function will receive the the actual values, Because as i notice on Vasiliy example i can't use any of the actual values passed inside the wrapper function but only the returned function is getting the values. – Aviel Fedida Jan 11 '14 at 12:03
  • Also i wanted to ask, Is the function inside a function is a type of closure? – Aviel Fedida Jan 11 '14 at 12:16
  • @uBlankText In the example above, when you inject `currencyBadFilterFn` somewhere, what you'll get is the returned function, not the wrapper function. The wrapper function doesn't have to return another function. It could return anything, an object, a string, a number. You can even do one-time setup work in the wrapper function before returning the inner function. – M.K. Safi Jan 11 '14 at 12:19
  • Yes i know i can return any value but if i will i won't be able to even use the value(10 in your example), So i don't have to use a function if the filter got now values to send? – Aviel Fedida Jan 11 '14 at 12:23
  • You can use this link i about to read it: http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – Aviel Fedida Jan 11 '14 at 12:26
  • @uBlankText I'm not sure about closures. When I hear "closure", I usually think IIFE, which this is not...but maybe I just misunderstand the technical definition of closure... – M.K. Safi Jan 11 '14 at 12:27
  • 1
    let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/45008/discussion-between-mk-safi-and-ublanktext) – M.K. Safi Jan 11 '14 at 12:27
2

This is how I did it.

myApp.filter("myCustomDateFormatter", ['MyFactoryWithCustomFunctions', function (InjectedCustomFunction) {
    return function (input) {
        return InjectedCustomFunction.GetDateFromDotNetJson(input);
    }
}]);
Mahesh
  • 3,727
  • 1
  • 39
  • 49