Pull to refresh

AngularJs $parse hacks

Reading time 3 min
Views 19K
Предлагаю перевод публикации «AngularJs $parse hacks».

В недрах AngularJs прячется одна маленькая и замечательная функция: $parse. Обычно она используется внутри фрэймворка для интерполяции значений, например при двусторонней провязке данных (two way data binding):

<p>Hello, {{ user.name }}</p>
// where user is an object in the scope

Наглядно этот простой пример можно посмотреть здесь.

Чтобы вычислить значение выражения user.name AngularJs вызовет $parse, после чего поместит результат в DOM.

$parse преобразовывает выражения, а не просто достает свойства объектов, например:

<p>{{ 'Hello ' + user.name }}</p>

Пример можно посмотреть здесь.

Вы можете использовать $parse в своем коде путем добавления зависимости в контроллер (или любую другую функцию). Вызов $parse подразумевает два шага: компиляция выражения в template функцию и последующий вызов этой самой функции с контекстом и локальными переменными. Обычно в роли контекста и локальных переменных выступает $scope объект:

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

Такое двухшаговое выполнение наблюдается и в других template библиотеках, таких как Handlebars.

В добавок $parse многое прощает, например, если $scope.user объект не существует, выражение нормально преобразуется, но возвращает undefined, которое отображается как пустая строка, пример здесь. Такое поведение функции $parse вылилось в следующие хаки:

Hack 1: безопасный доступ вложенных свойст


Если у нас есть объект со значением свойства, которое может быть null, доступ к вложенным свойствам этого свойства принуждает к всякого рода проверкам:

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;
  }

Конечно можно использовать стороннюю библиотеку вроде l33teral и обвертывать объекты для безопасного доступа:

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

Но если вы уже используете AngularJs, то просто используйте $parse:

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

Полный пример. Если свойство не существует, вызов вернет undefined:

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

Так же можно назначить первый шаг функцию в переменную, во избежание повторного компилирования выражения.

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


Hack 2: отправка логики с бакэнда клиенту


Если требуется динамически что-то запускать (вычислять) на клиенте, можно отправлять логику с сервера в виде строки. В строке помимо методов можно определять и локальные переменные, для этого $parse вызывается с 2-мя аргументами (контекст и локальные переменные):

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

Полный пример.

Аргумент data может переопределять свойства в контексте аргумента ops, но я рекомендую держать методы отдельно от данных для более понятной реализации.

Hack 3: Spreadsheet за 20 минут


Для демонстрации мощи AngularJs во всей красе я люблю ссылаться на David Graunke's spreadsheet example. Это супер потрясающий, пример который использует $parse для динамического преобразования выражений внутри каждой ячейки. Выражения могут ссылаться на значения других ячеек, которые в свою очередь могут ссылаться на другие ячейки и т.д. Основная логика этого примера заключается в том, что все ячейки находятся в scope, а функция coumpute вызывается каждый раз, когда значение любой ячейки меняется.

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 в действии.
Tags:
Hubs:
+12
Comments 6
Comments Comments 6

Articles