- PVSM.RU - https://www.pvsm.ru -

AngularJs $parse hacks

Предлагаю перевод публикации «AngularJs $parse hacks» [1].

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

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

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

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

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

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

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

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

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

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

В добавок $parse многое прощает, например, если $scope.user объект не существует, выражение нормально преобразуется, но возвращает undefined, которое отображается как пустая строка, пример здесь [7]. Такое поведение функции $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 [8] и обвертывать объекты для безопасного доступа:

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

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

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

Полный пример [9]. Если свойство не существует, вызов вернет 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

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

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

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

Для демонстрации мощи AngularJs во всей красе я люблю ссылаться на David Graunke's spreadsheet example [11]. Это супер потрясающий, пример который использует $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 [12] в действии.

Автор: VBauer

Источник [13]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/75289

Ссылки в тексте:

[1] «AngularJs $parse hacks»: http://bahmutov.calepin.co/angularjs-parse-hacks.html

[2] $parse: https://docs.angularjs.org/api/ng/service/$parse

[3] здесь: http://jsbin.com/pitoy/1/edit?html,output

[4] выражения: https://docs.angularjs.org/guide/expression

[5] здесь: http://jsbin.com/pitoy/2/edit?html,output

[6] Handlebars: http://handlebarsjs.com/

[7] здесь: http://jsbin.com/pitoy/3/edit?html,output

[8] l33teral: https://github.com/nicholascloud/l33teral

[9] пример: http://jsbin.com/pitoy/4/edit?html,output

[10] пример: http://jsbin.com/pitoy/5/edit?html,output

[11] David Graunke's spreadsheet example: https://twitter.com/graunked

[12] Spreadsheet: http://thomasstreet.com/blog/legacy/versions/865351.html

[13] Источник: http://habrahabr.ru/post/244001/