Javascript-форум (https://javascript.ru/forum/)
-   Angular.js (https://javascript.ru/forum/angular/)
-   -   Сериализация функций интерполяции (https://javascript.ru/forum/angular/46878-serializaciya-funkcijj-interpolyacii.html)

Shitbox2 27.04.2014 23:10

Сериализация функций интерполяции
 
В многоязычном приложении компилирую переводы, содержащие логику, в ангуляровские выражения:
"{{{0:1,8:1}[n]==1 ? {0:'%нет тарелок '+{'0':'добави’ло = 5', '1':'добавил', '2':'добавила'}[s]+'',8:'тарелочки!'}[n] : {one:''+n+' тарелка',few:''+n+' ’тарелки',many:''+n+' ″тарелок″',other:''+n+' сколько-то тарелок'}[n%1==0 ? (n%10==1 && n%100!=11 ? 'one' : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 'few' : 'many') : 'other']}}

Далее Ангуляр читает JSON со строками, парсит и выполняет.

Хочу парсить заранее и в JSON класть сериализованные функции, для чего поставил Ангуляр на node.js и обрабатываю строки методом $interpolate.

Проблема в том, что сериализация полученной функции методом toString() выдает такое:
function (context) {
          try {
            for(var i = 0, ii = length, part; i<ii; i++) {
              if (typeof (part = parts[i]) == 'function') {
                part = part(context);
                if (trustedContext) {
                  part = $sce.getTrusted(trustedContext, part);
                } else {
                  part = $sce.valueOf(part);
                }
                if (part === null || isUndefined(part)) {
                  part = '';
                } else if (typeof part != 'string') {
                  part = toJson(part);
                }
              }
              concat[i] = part;
            }
            return concat.join('');
          }
          catch(err) {
            var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
                err.toString());
            $exceptionHandler(newErr);
          }
        }

Можно как-нибудь подтянуть зависимости или сделать так, чтобы Ангуляр на клиенте смог выполнить такую функцию?

FireVolkhov 28.04.2014 10:36

Как ты себе сам представляешь "как-нибудь подтянуть зависимости"?
О каких зависимостях идет речь? Модули Angular'а или переменные?

В Angular'е есть injecter, может тебе поможет
angular.injector(['ng']).invoke(['$filter', function($f){
     console.log($f('number')(1.2345, 2));
}]);

Shitbox2 28.04.2014 11:05

Можно и с инжектором, но тогда огого-парсер придется писать. Оставим пока этот вариант. Допустим я все-же собрал работоспособную функцию, сериализовал. Как ее раскукожить на клиенте? new Function? eval?

Мои эксперименты показали, что для небольших выражений new Function в несколько раз медленнее чем парсинг их через $interpolate...

FireVolkhov 28.04.2014 13:49

То есть ты хочешь
server_on_node: "{{...}}" -> function -> string -> http_response
client_in_browser: http_response -> string -> function -> выполнить функцию и получить результат
Если так, то тебе не кажется, что отправить "{{...}}" быстрей и дешевле по ресурсам, чем трансформация "{{...}}" -> function -> string -> function -> function();?

Shitbox2 28.04.2014 21:23

Кажется. Так и есть, на самом деле.

Поэтому сейчас вместо того, чтобы делать функцию из строки на клиенте планирую внедрить эту функцию непосредственно в js-файл приложения. Например, собрать грунтом отдельный ангуляровский модуль с константой и подключить его как зависимость. Но задача остается: функцию нужно как-то перевести в строку, чтобы грунтовский сборщик мог с ней работать.

FireVolkhov 29.04.2014 07:38

Как я вижу это процесс, собрать все переменные из текста функции, через eval() получить сами переменные, если будут функции делаем тоже самое.

Shitbox2 29.04.2014 08:12

Скорее это я вызвал путаницу :)

Сейчас используется вариант №1 (он, кстати, оказался не таким уж медленным. Обработка каждого выражения на клиенте занимает не больше 1 мс)

Пробовал использовать вариант №2 (только функцию собирал в строку вручную), он оказался медленнее

Теперь думаю про вариант №3
1. сервер через $interpolate делает функцию из строки str,
2. полученную функцию конвертирует в строку (тоже не прадставляю как это сделать),
3. собирает все функции в ангуляровский модуль и включает его в файл приложения,
4. клиент при загрузке проверяет, что есть модуль с функциями и использует их параметром $scope(или что-то другое) и получает текст,
5. который показывает пользователю.

Это по-любому будет самый быстрый вариант, но сериализовать, полученную через $interpolate функцию, скорее всего не получится. Наверное, проще будет распарсить выражение и вручную превратить его в функцию...

P.S. О ресурсах сервера можно не беспокоиться, т.к. это не бэкенд сервер, а машина разработчика.

FireVolkhov 29.04.2014 09:10

Прочитав немного Замыкания, функции изнутри и немного стандарта ECMA-262, тебе придется писать свой $interpolate, так как окружение функции хранится в С++, из JS его ни как не получить.

FireVolkhov 29.04.2014 09:15

Потом, когда закончишь, можешь выложить результаты тестов на производительность?

Shitbox2 30.04.2014 00:45

Думаю, проще просто обернуть это выражение в функцию (ангуляровские выражения это просто урезанный js). только предварительно заменить все переменные с n на context.n (контекст приходит в качестве параметра) и вырезать опасные слова... Если займусь этим, обязательно выложу тесты


Часовой пояс GMT +3, время: 20:43.