Not today...

comments

Tuto

Angular $parse

Tagged javascript , angular , web

Hey there, I have started to be tired of weak example of angular power, so I will go deeper on angular services and directives and wrote some articles about it.

The service $parse is the one who runs on the html to bind data with your javascript. It provides a lot of useful features which can be really interesting especially with directive manipulation.

So, we will illustrate with some examples:

// This notation allow us to stay in pure javascript world
angular.injector(['ng'])
.invoke(function ($parse, $rootScope) {

  // new clean scope (optionnal, it will work directly on $rootScope)
  var $scope = $rootScope.$new();

  // allocate simple value
  $scope.foo = 'bar';

  // here the magic
  var foo = $parse('foo');
  /*
    The function which has been returned look for a context to interpolate the
    value
  */
  console.log(foo($scope));
  // display => bar

  /*
    Redefine foo value
   */
  foo.assign($scope, 'foo');

  /*
    $scope has changed Oo
   */
  console.log($scope.foo);
  // display => foo
});

Pretty cool uh? But wait there is more

angular.injector(['ng'])
.invoke(function ($parse, $rootScope) {
  var $scope = $rootScope.$new();

  // allocate function
  $scope.foo = function (argFoo) {
    return argFoo;
  };

  // allocate value
  $scope.someValue = 'foo'

  var foo = $parse('foo(someValue)');

  console.log(foo($scope));
  // display => foo
});

It also interpolates functions?! But wait there is more

angular.injector(['ng'])
.invoke(function ($parse, $rootScope) {
  var $scope = $rootScope.$new();

  //allocate function
  $scope.foo = function (argFoo) {
    return argFoo;
  };

  //allocate value
  $scope.someValue = 'foo';

  var foo = $parse('foo(someValue)');
  /*
    The function returned by the $parse service has a second argument wich will
    override the context in case of conflict
   */
  console.log(foo($scope, {someValue: 'bar'}));
  // display => bar
});

Now you will ask: Is there a use case for that? Of course, passing additionnal arguments in directive is possible. For example, the ng-click directive (doc)

This pattern allows you to retrieve the $event object directly in your function handler.

Here is a simplification with explanation of the ng-click directive

angular.module('myModule', [])
.directive('myNgClick', ['$parse', function ($parse) {
  return {
    link: function (scope, elt, attr) {
      /*
       Gets the function you have passed to ng-click directive
       Parse returns a function which has a context and extra params which
       overrides the context
      */
      var handler = $parse(attr['myNgClick']);

      /*
      here you bind on click event you can look at the documentation
      https://docs.angularjs.org/api/ng/function/angular.element
      */
      elt.on('click', function (event) {

        //callback is here for the explanation
        var callback = function () {

          /*
           Here handler will do the following, it will call the dedicated
           function and fill the arguments with the elements found in the scope
           (if possible), the second argument will override the $event attribute
           in the scope (if there is some) and provide the event element of the
           click
          */
          handler(scope, {$event: event});
        };

        //$apply force angular to run a digest cycle in order to propagate the
        //changes
        scope.$apply(callback);
      });
    }
  };
}]);

And it will work, example => here

You can also find example of the two first parts here

Hope you enjoyed the trip, see ya o/