CuriousMind
CuriousMind

Reputation: 34155

AngularJs & Javascript nested functions & their invocation

Consider the following code

<body ng-app="myApp">
  <h1>Hello {{name}}</h1>
  <h2>reverse of your name is {{ name | reverse }}</h2>
  <input type="text" ng-model="name">
  <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.js'></script>
  <script>
  angular.module('myApp', [])
    .filter('reverse', function(){
      return function(str){
        return str.split('').reverse().join('');
      }
    });
  </script>
</body>

The interested bit here is the reverse filter. Here is what I think its doing:

  1. calls angular.filter() with two args. arg1: string & arg2: function(anonymous or no-name function to be precise)
  2. arg2 function doesn't take a argument and instead nested another anonymous function inside it.
  3. This nested anonymous function does takes single argument - the text or string on which this filter would apply.
  4. this nested anonymous function takes the string, reverses it.

Q1. Is my understanding correct?

Q2. What the difference between: normal version:

angular.filter('reverse', function(str){
  return str.split('').reverse().join('');
});

nested version:

angular.filter('reverse', function(str){
      return function(str){
        return str.split('').reverse().join('');
      }
    });

Q3. Why is extra level of function nesting useful & under what circumstances will I return the value directly. Or return a function which then does return the actuall result?

Q4. How does this affects scope? does closures have any role to play here?

JSFiddle: http://jsfiddle.net/C7EDv/

Upvotes: 3

Views: 2074

Answers (1)

KayakDave
KayakDave

Reputation: 24676

(Q1.1) Right- two args, a string with the name of the filter and...

(Q2/3) The second parameter to filter (arg2) is a constructor (or "factory") function. It is only executed once upon creation of the filter.

The constructor function must return a function. That returned function will be executed when the filter it is associated with is used. Put another way, the returned function is what is injected (using $injector) in to the filter (http://docs.angularjs.org/api/ng.$filterProvider)

I've added comments below detailing this:

angular.filter('reverse', function(service){
      // This is run only once- upon creation
      return function(str){
        // This is run every time the filter is called
        return str.split('').reverse().join('');
      }
});

(Q3) You'll always use this form ("the nested form") otherwise you'll get an error (Object ... has no method 'apply') as Angular needs a function to be returned which it can call (using apply()) whenever the filter is used. This works exactly like all providers in Angular including services.

(Q2) Were it possible to do what you called the "normal version" then the filter would run only once, upon creation. And whatever return value it got would be used for every invocation of the filter. Since having a filter that always returns the same value wouldn't be useful very often Angular instead uses this javascript constructor pattern (which you'll see used elsewhere in JS) so that each use of the filter results in a new invocation of the associated function.

(Q1.4)Yes, the returned function does reverse the string. Here's a nice 2 minute video on exactly this filter: http://www.thinkster.io/pick/EnA7rkqH82/angularjs-filters

(Q1.2/3)Should your filter use any services you'd pass them in where I used service above (the above link has an example of that). The parameter str is the input to the filter like you noted.

(Q4) The "run once" area does create a closure- so you can use it like you would any other closure.

Upvotes: 3

Related Questions