[Из песочницы] AngularJs $parse hacks
Предлагаю перевод публикации «AngularJs $parse hacks».В недрах AngularJs прячется одна маленькая и замечательная функция: $parse. Обычно она используется внутри фрэймворка для интерполяции значений, например при двусторонней провязке данных (two way data binding):
Hello, {{ user.name }}
// where user is an object in the scope Наглядно этот простой пример можно посмотреть здесь.Чтобы вычислить значение выражения user.name AngularJs вызовет $parse, после чего поместит результат в DOM.$parse преобразовывает выражения, а не просто достает свойства объектов, например:
{{ 'Hello ' + user.name }}
Пример можно посмотреть здесь.Вы можете использовать $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 в действии.