AngularJs $ parse hacks

I propose a translation of the publication "AngularJs $ parse hacks" .

In the bowels of AngularJs, there is one small and wonderful feature: $ parse . Usually it is used inside the framework to interpolate values, for example, when two-way data binding:

Hello, {{ user.name }}

// where user is an object in the scope

This simple example can be clearly seen here .

To calculate the value of the user.name expression , AngularJs will call $ parse, and then put the result in the DOM.

$ parse converts expressions , and not just pulls out the properties of objects, for example:

{{ 'Hello ' + user.name }}


An example can be seen here .

You can use $ parse in your code by adding a dependency to the controller (or any other function). Calling $ parse involves two steps: compiling the expression into a template function and then calling this function itself with the context and local variables. Usually, the scope object is a $ scope object:

function controller($scope, $parse) {
  $scope.user = {
    name: 'Joe'
  };
  var template = $parse('Hello + user.name');
  var msg = template($scope); // Hello Joe
}

This two-step execution is also observed in other template libraries, such as Handlebars .

In addition, $ parse forgives a lot, for example, if the $ scope.user object does not exist, the expression normally converts, but returns undefined, which displays as an empty string, an example is here . This behavior of the $ parse function resulted in the following hacks:

Hack 1: secure access to embedded properties


If we have an object with a property value, which can be null, access to the attached properties of this property forces all kinds of checks:

var foo = {
  bar: {
    baz: {
      name: 'baz'
    }
  }
};
var name;
if (typeof foo === 'object' &&
  typeof foo.bar === 'object' &&
  typeof foo.bar.baz === 'object') {
    name = foo.bar.baz.name;
  }

Of course, you can use a third-party library like l33teral and wrap objects for safe access:

var leet = require('l33teral');
var fooL = leet(foo);
var name = fooL.tap('bar.baz.name');

But if you are already using AngularJs, then just use $ parse:

var name = $parse('bar.baz.name')(foo);

Full example . If the property does not exist, the call will return undefined:

$parse('bar.baz2.name')(foo); // returns undefined

You can also assign the first step to a function in a variable, to avoid recompiling the expression.

var getName = $parse('bar.baz.name');
...
getName(foo);


Hack 2: sending backend logic to a client


If you want to dynamically run something (calculate) on the client, you can send logic from the server as a string. In addition to methods, you can define local variables in the line, for this $ parse is called with 2 arguments (context and local variables):

var ops = {
  add: function (a, b) { return a + b; },
  mul: function (a, b) { return a * b; }
};
var logic = 'mul(add(a, 1), b)';
var data = {
  a: 10,
  b: 4
};
var result = $parse(logic)(ops, data); // 44

Full example .

The data argument can override properties in the context of the ops argument, but I recommend keeping the methods separate from the data for a more understandable implementation.

Hack 3: Spreadsheet in 20 minutes


To demonstrate the power of AngularJs in all its glory, I like to refer to David Graunke's spreadsheet example . This is super awesome, an example that uses $ parse to dynamically transform expressions within each cell. Expressions can refer to the values ​​of other cells, which in turn can refer to other cells, etc. The main logic of this example is that all cells are in scope, and the coumpute function is called every time the value of any cell changes.

function sheetController($scope, $parse) {
  $scope.columns = ['A', 'B', 'C', 'D'];
  $scope.rows = [1, 2, 3, 4];
  $scope.cells = {}; // will be filled with row x column objects
  $scope.compute = function(cell) {
    return $parse($scope.cells[cell])($scope);
  };
}

Spreadsheet in action.

Also popular now: