Удовольствие сомнительное, но в моем случае использование Google Apps Script было необходимым условием задачи, а AngularJS в силу ряда причин, не имеющих прямого отношения к настоящему посту, пришелся очень даже кстати.
В общем если вы приняли решение встать на скользкий путь создания мэшапов на GAS и ангуляр, думаю освоение следующего материала может сократить количество неприятных ощущений, которые вы без сомнения испытаете в процессе применения озвученных выше инструментов.
На этом прелюдия завершена, переходим к содержательной части.
Тестовое приложение выглядит следующим образом:
... и состоит из:
- Код.js - серверный javascript-код
- index.html - html-код
- js.html - клиентский javascript-код
- css.html - css-код
Код.js - html-шаблон отдаем обязательно в "нативном" режиме "песочницы", для инжекта кода, в нашем случае из файлов js.html и css.html, используем функцию require (назовите ее на ваш вкус):
function doGet(e) { var t = HtmlService.createTemplateFromFile('index'); return t.evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE); } function require(filename) { return HtmlService.createHtmlOutputFromFile(filename).getContent(); }index.html - в самом начале инжектим код клиентских js и css:
<?!= require('css'); ?> <?!= require('js'); ?> <div class="container" ng-app=""> <h1>Hello <span ng-bind="name"></span></h1> <input type="text" ng-model="name" placeholder="Enter your name"> </div>
js.html - здесь обязательно ссылка на полный, не минифицированный, код ангуляра:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
css.html - забутстрапим для красоты:
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
Разворачиваем веб-приложение, тестим:
Ура, мы все-таки скрестили ангуляр и GAS! Дальше расскажу как получить данные сервера на клиенте.
Добавим в файл Код.gs функцию по имени getData:
function doGet(e) { var t = HtmlService.createTemplateFromFile('index'); return t.evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE); } function require(filename) { return HtmlService.createHtmlOutputFromFile(filename).getContent(); } function getData() { return [['a','b','c'],[1,'qwe','rty'],[2,'asd','fgh'],[3,'zxc','vbn']]; }
В контексте этого поста неважно как мы получили эти данные, главное что они есть.
Пытаемся отобразить данные, полученные с сервера в таблице - редактируем файл index.html:
<?!= require('css'); ?> <?!= require('js'); ?> <div class="container" ng-app="app" ng-controller="MainCtrl"> <h1>Hello <span ng-bind="name"></span></h1> <input type="text" ng-model="name" placeholder="Enter your name"> <table class="table table-hover"> <thead> <tr> <th ng-repeat="key in heading">{{key}}</th> </tr> </thead> <tbody> <tr ng-repeat="arr in list"> <td ng-repeat="i in arr">{{arr[$index]}}</td> </tr> </tbody> </table> </div>
Теперь самое интересное - редактируем файл js.html - получаем данные с сервера и втыкаем их в $scope.list приложения (заголовок таблицы нас пока не интересует):
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script> <script> angular.module('app', []).controller('MainCtrl', function($scope) { google.script.run.withSuccessHandler(gotData).withFailureHandler(gotError).getData(); $scope.heading = []; $scope.list = []; function gotData(data) { console.log('data: ' + JSON.stringify(data)); $scope.list = data; } function gotError(e) { console.log('error: ' + e.message); } }); </script>
И ничего не случилось, хотя данные с сервера прилетели:
Пытаемся обновить данные - редактируем файл js.html - $scope.apply() :
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script> <script> angular.module('app', []).controller('MainCtrl', function($scope) { google.script.run.withSuccessHandler(gotData).withFailureHandler(gotError).getData(); $scope.heading = []; $scope.list = []; function gotData(data) { console.log('data: ' + JSON.stringify(data)); $scope.list = data; $scope.$apply(); } function gotError(e) { console.log('error: ' + e.message); } }); </script>
И получаем ошибку на клиенте:
Пчелы начали что-то подозревать :). Должен признать что следующая итерация заняла у меня не менее получаса, редактируем файл js.html еще раз:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script> <script> angular.module('app', []).controller('MainCtrl', function($scope) { google.script.run.withSuccessHandler(gotData).withFailureHandler(gotError).getData(); $scope.heading = []; $scope.list = []; function gotData(data) { console.log('data: ' + JSON.stringify(data)); console.log('data isExtensible: ' + Object.isExtensible(data)); console.log('data isSealed: ' + Object.isSealed(data)); console.log('data isFrozen: ' + Object.isFrozen(data)); $scope.list = data; $scope.$apply(); } function gotError(e) { console.log('error: ' + e.message); } }); </script>
Обращаю внимание на скриншот - не знаю где и с какой целью происходит "заморозка" данных, короче ничего не остается кроме как растасовать данные, полученные с сервера в аргументе data функции gotData путем перебора свойств аргумента, как-то так:
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script> <script> angular.module('app', []).controller('MainCtrl', function($scope) { google.script.run.withSuccessHandler(gotData).withFailureHandler(gotError).getData(); $scope.heading = []; $scope.list = []; function gotData(data) { console.log('data: ' + JSON.stringify(data)); var heading = data[0], i = 0, hLen = heading.length, j, dLen = data.length, arr; for (; i < hLen; i += 1) { $scope.heading.push(heading[i]); } for (i = 1; i < dLen; i += 1) { arr = []; for (j = 0; j < hLen; j += 1) { arr.push(data[i][j]); } $scope.list.push(arr); } $scope.$apply(); } function gotError(e) { console.log('error: ' + e.message); } }); </script>
Еще пара вариантов "разморозки" попроще:
data = JSON.parse(JSON.stringify(data));
data = angular.copy(data);
Только после подобных манипуляций мы можем рассчитывать на результат:
На этом тему мэшапа Google Apps Script и AngularJS считаю раскрытой. Кого заинтересовали подробности этого извращения - пишите в комменты, по возможности постараюсь ответить.
p.s.: Если приложение по ссылке не отрабатывает как ожидается - попробуйте обновить страницу в браузере - это
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации