Angular Js и Google Api client.js (gapi)
Мне потребовался один день, чтобы заставить это работать, так что я думаю, что мой опыт может быть полезным от кого-то. И, возможно, некоторые другие найдут улучшение.
Итак, я начинаю angularJS два дня назад. И я хочу, чтобы он работал с конечными точками Google Cloud для создания внутреннего интерфейса. Вот беда для меня.
Клиент javascript для gapi поставляется с асинхронной загрузкой, поэтому угловая инициализация приведет к сбою с неопределенностью gapi.
Таким образом, вам нужно выполнить угловую загрузку при инициализации gapi:
- удалить ng-app="myApp"
- добавлять
<script src="https://apis.google.com/js/client.js?onload=googleOnLoadCallback"></script>
Добавьте обратный звонок:
function googleOnLoadCallback(){ var apisToLoad = 1; // must match number of calls to gapi.client.load() var gCallback = function() { if (--apisToLoad == 0) { //Manual bootstraping of the application var $injector = angular.bootstrap(document, ['myApp']); console.log('Angular bootstrap complete ' + gapi); }; }; gapi.client.load('helloWorld', 'v1', gCallback, '//' + window.location.host + '/_ah/api'); }
Чувствовать себя хорошо, но как насчет звонка?
Итак, вот контроллер:
angular.module('myApp.controllers', []).
.controller('MyCtrl', ['$scope' ,'helloWorldService',
function($scope,greetingsService) {
helloWorldService.loadData($scope);
}]);
А вот и сервис:
angular.module('myApp.services', [])
service('helloWorldService', [function() {
this.loadData = function($scope) {
//Async call to google service
gapi.client.helloWorld.greetings.listGreeting().execute(
function(resp) {
if (!resp.code) {
console.debug(resp);
$scope.greetings = resp.items;
// Because it's a callback,
// we need to notify angular of the data refresh...
$scope.$apply();
}
});
};
}]);
И волшебным образом ваша страница обновляется благодаря Angular.
Не стесняйтесь отмечать везде, где я ошибаюсь.
8 ответов
Вместо начальной загрузки или установки таймаута наиболее эффективно разрешить загрузку Angular до / во время выполнения запросов к серверу. Я следовал совету, описанному в AngularJS + Cloud Endpoints: Рецепт для создания современных веб-приложений, который делает следующее.
Держите ваши ng-app
директива как обычно (без начальной загрузки)
<html ng-app="myApp">
<head>
<script src="angular.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
</head>
<body ng-show="backendReady">
Создайте глобальную переменную для функции обратного вызова GAPI в любом месте вашего JS
var app = angular.module('myApp', []);
var init = function() {
window.initGapi();
}
app.controller('MainController', function($scope, $window, gapiService) {
var postInitiation = function() {
// load all your assets
}
$window.initGapi = function() {
gapiService.initGapi(postInitiation);
}
});
app.service('gapiService', function() {
this.initGapi = function(postInitiation) {
gapi.client.load('helloWorld', 'v1', postInitiation, restURL);
}
});
По ссылке выше:
Причина, по которой вы не хотите выполнять инициализацию в первом методе init(), заключается в том, что вы можете поместить как можно больше кода в мир AngularJS, например, контроллеры, службы и директивы. В результате вы можете использовать всю мощь AngularJS и иметь все свои модульные тесты, интеграционные тесты и так далее.
Это может выглядеть как обходной путь, но он оптимизирует скорость, тестируемость и разделение проблем.
Хороший пост и спасибо! Этот подход работал для меня. Может иметь значение, в каком порядке код появляется в вашем файле index.html. Это не сработало для меня, пока у меня не было вещей в этом порядке.
...
<script>
function googleOnLoadCallback(){
alert('googleOnLoadCallback called');
var apisToLoad = 1; // must match number of calls to gapi.client.load()
var gCallback = function() {
if (--apisToLoad == 0) {
//Manual bootstraping of the application
var $injector = angular.bootstrap(document, ["myApp"]);
console.log("myApp bootstrap complete " + gapi);
};
};
gapi.client.setApiKey("my_client_id");
gapi.client.load("translate", "v2", gCallback);
}
</script>
<!-- See https://developers.google.com/api-client-library/javascript/samples/samples -->
<script src="https://apis.google.com/js/client.js?onload=googleOnLoadCallback"></script>
</head>
Хотя в значительной степени о прогрессе, возможно, также стоит упомянуть angular-googleapi, который прекрасно сочетается с некоторыми вызовами Google Calendar и Google Plus API и легко расширяется.
Вам нужно добавить этот бит в свой контроллер при проверке авторизации:
$scope.authenticated = false;
$scope.$on("google:authenticated", function(){
$scope.authenticated = true;
$scope.$on('googleCalendar:loaded', function(){
# work your magic here
# $scope.calendars = googleCalendar.listCalendars();
# $scope.$apply();
});
});
function checkAuth() {
setTimeout(function(){
gapi.auth === undefined ? checkAuth() : googleLogin.checkAuth();
}, 20);
}
checkAuth();
Я написал простую директиву для асинхронной загрузки API Google Map:
// js/directives/gmapAsync.js
(function(){
'use strict';
angular.module('app').directive('gmapAsync',
['$window', '$rootScope', gmapAsync]
);
function gmapAsync($window, $rootScope){
var gmapScript = $window.document.createElement('script');
$window.onGmapScriptLoaded = function(){
console.log('google maps script loaded');
$rootScope.gmapApiLoaded = true;
$rootScope.$broadcast('gmap.api.loaded');
};
return {
restrict: 'A',
transclude: false,
scope:false,
link: function(scope, element, attributes){
if (navigator.onLine) {
appendScript();
} else {
$window.addEventListener('online',appendScript);
}
function appendScript(){
gmapScript.type = 'text/javascript';
gmapScript.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&' + 'callback=onGmapScriptLoaded';
$window.document.body.appendChild(gmapScript);
}
}
};
}
})();
Затем в вашем главном контроллере вы можете обработать событие:
// js/controllers/AppCtrl.js
(function(){
'use strict';
angular.module('app').controller('AppCtrl',[$scope,AppCtrl])
function AppCtrl($scope){
$scope.$on('gmap.api.loaded',function(){
// your stuff to init after the api is loaded
});
}
})();
Вам просто нужно объявить директиву в теге body:
<!DOCTYPE html>
<html>
<head></head>
<body data-ng-app="app" data-gmap-async data-ng-controller="AppCtrl">
<!-- template body -->
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/controllers/AppCtrl.js"></script>
<script type="text/javascript" src="js/directives/gmapAsync.js"></script>
</body>
</html>
Я использовал решение, похожее на willlma, но мое приложение использует UI Router, поэтому неизвестно, какой контроллер будет вызываться.
Я смог решить это с обещанием Javascript.
index.html
<html ng-app="myApp">
<head>
<script src="angular.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<script src="https://apis.google.com/js/client.js?onload=init">
</head>
app.js
var app = angular.module('myApp', []);
app.controller('MainController', function($scope, gapiService) {
gapiService.then(function(gapi) {
// You can use gapi normally here;
});
});
app.service('gapiService', function($window) {
return new Promise(function(resolve, reject) {
if ($window.gapi !== undefined) {
console.log("Have gapi already");
resolve($window.gapi);
} else {
console.log("Waiting for gapi");
$window.init = function() {
resolve($window.gapi);
}
}
});
});
Взгляните на это: https://github.com/canemacchina/angular-google-client.
Я написал этот модуль для использования Google Api или Google Cloud Endpoint в приложении Angular.
Я сделал следующее
GAPI-service.js
'use strict';
app.factory('Gapi', ['ENV', function(ENV) {
return {
load: function load() {
console.log('loading google apis...');
if (typeof gapi.client === 'undefined') {
setTimeout(load, 500);
} else {
gapi.client.setApiKey(ENV.googleToken);
gapi.client.load('storage', 'v1', function() {
console.log('loaded! :)');
var request = gapi.client.storage.buckets.list({ project: ''});
console.log(request);
request.execute(function(response) { console.log(response); });
});
}
}
};
}]);
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>"Txtbinge"</title>
</head>
<body ng-app="myApp">
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="scripts/client.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/gapi-service.js"></script>
</body>
</html>
controllers.js
'use strict';
app.controller('AppController', function($scope, $state, Camera, Gapi) {
Gapi.load();
});
У меня была такая же проблема. Положить этот код на моем заводе работал
var initialize = function() {
if(gapi.client == undefined) {
setTimeout(function() {
initialize()
}, 1000);
} else {
gapi.client.setApiKey("<api_key>");
gapi.client.load('youtube', 'v3').then(function() {
console.log("youtube is ready")
});
}
};
initialize()
По сути, проблема заключается в попытке вызвать gapi.client до его загрузки. Если вы просто проверяете, загружен ли он, а если нет, то повторите попытку через секунду (вы можете установить время для чего угодно, установите его ниже, если вы ожидаете, что пользователю это потребуется относительно быстро после загрузки страницы).
Я боролся с этим некоторое время, и это все, что сработало для меня... Надеюсь, это поможет!