В этом уроке мы будем создавать приложение Cordova, которое подключается к API Facebook с помощью официального плагина Facebook для Cordova . Мы расскажем, как войти в систему и выйти из Facebook, использовать диалоги и совершать звонки в Graph API. Обратите внимание, что это руководство не для начинающих, я предполагаю, что вы уже настроили свой компьютер для работы с Cordova и развертывания на устройстве Android.
Создать приложение для Facebook
Сначала создайте приложение на веб-сайте разработчиков Facebook . Наведите указатель мыши на меню « Мои приложения» и выберите « Добавить новое приложение» . Это откроет модальное окно, которое позволяет вам выбрать платформу для вашего приложения. В этом случае мы будем развертывать на устройстве Android, поэтому выберите Android в модальном окне.
Введите имя для приложения и нажмите кнопку « Создать новый идентификатор приложения Facebook» .
Вам будет задан вопрос, является ли приложение тестовой версией существующего приложения, оставьте его по умолчанию « нет» . Другой вариант — категория, выберите приложения для страниц, затем нажмите кнопку « Создать идентификатор приложения» .
Далее вам будет предложено добавить SDK Facebook в ваш проект. Нам не нужно делать это для нашего приложения, поэтому продолжайте прокручивать страницу, пока не найдете раздел Расскажите нам о своем проекте Android . Введите com.yourname.appname в качестве имени пакета, а затем com.yourname.appname.MainActivity в качестве имени класса активности по умолчанию, заменив его соответствующими значениями для вашего проекта.
Как только это будет завершено, нам нужно добавить ключевые хэши разработки и выпуска. Они проверяют подлинность вашего приложения, так что Facebook уверен, что вы являетесь автором приложения, а запросы к их API поступают от вас.
Прежде чем вы сможете сгенерировать хеш, вам сначала нужно хранилище ключей. Вы можете создать его, выполнив следующую команду в окне терминала.
keytool -genkey -v -keystore cordova-social.keystore -alias cordovaSocial -keyalg RSA -keysize 2048 -validity 10000
Получить хеш из хранилища ключей:
keytool -exportcert -alias androiddebugkey -keystore cordova-social.keystore | openssl sha1 -binary | openssl base64
Это должно попросить вас пароль. Просто используйте тот же пароль, который использовался при создании хранилища ключей. После ввода он должен вернуть хеш для этого хранилища ключей. Скопируйте и вставьте его в поле хэшей разработки.
Нажмите « Далее» и обновите страницу. Ваше приложение должно появиться в списке, когда вы нажимаете на меню Мои приложения . Запишите идентификатор приложения, так как он понадобится нам позже, когда мы установим плагин Facebook.
Сборка приложения
Теперь мы готовы построить приложение. Установите Ionic (который мы будем использовать для создания нашего приложения) с помощью следующей команды:
npm install -g ionic
Создайте новое пустое приложение:
ionic start your_app_name blank cd your_app_name
Добавьте платформу Android:
ionic platform add android
Установка зависимостей
Далее нам нужно установить следующие плагины:
Кордова-Plugin-Whitelist
Позволяет нам контролировать, к каким доменам приложение может отправлять запросы.
Кордова-Plugin-камера
Позволяет нам использовать камеру устройства для захвата фотографий.
org.apache.cordova.file-Transfer
Позволяет загружать фотографии, снятые плагином камеры, на удаленный сервер.
Примечание . Мы используем старую версию этого плагина, поскольку последняя версия несовместима с текущей версией Cordova (5.0.0) на момент написания этой статьи. Если вы читаете это в будущем, вы можете попробовать использовать следующую команду: cordova plugin add cordova-plugin-file-transfer
и посмотреть, работает ли он для вас. В противном случае используйте команду ниже.
PhoneGap-facebook-плагин
Официальный плагин Facebook, используемый для выполнения различных операций с API Facebook.
Установите плагины с помощью следующих команд:
cordova plugin add cordova-plugin-whitelist cordova plugin add cordova-plugin-camera cordova plugin add org.apache.cordova.file-transfer cordova plugin add https://github.com/Wizcorp/phonegap-facebook-plugin --variable APP_ID=YOUR_FACEBOOK_APP_ID --variable APP_NAME=YOUR_APP_NAME
Как только это будет завершено, нам нужно установить внешнюю зависимость под названием Angular Local Storage . Это позволяет нам работать с локальным хранилищем для кэширования данных в нашем приложении. Вы можете установить Angular Local Storage через Bower с помощью следующей команды:
bower install angular-local-storage --save
Ionic устанавливает компоненты Bower в каталог www / lib . Вы можете проверить место сохранения, открыв файл .bowerrc в корневом каталоге проекта.
{ "directory": "www/lib" }
Связывание файлов
На данный момент мы будем в основном работать внутри каталога www . Откройте файл index.html в этом каталоге.
Ссылка на скрипт Angular Local Storage ниже файла ionic.bundle.js .
<script src="lib/ionic/js/ionic.bundle.js"></script> <script src="lib/angular-local-storage/dist/angular-local-storage.min.js"></script>
Ниже файла app.js ссылки на следующие скрипты:
<script src="js/app.js"></script> <!--services--> <script src="js/services/RequestsService.js"></script> <script src="js/services/CameraService.js"></script> <!--controllers--> <script src="js/controllers/LoginController.js"></script> <script src="js/controllers/DialogController.js"></script> <script src="js/controllers/UserDetailsController.js"></script>
Позже я объясню, что делает каждый из этих сценариев. Сервисные сценарии предназначены для выполнения HTTP-запросов и в качестве оболочки для плагина Camera. Скрипты контроллера предназначены для разных страниц приложения.
Измените <body>
(и его содержимое), чтобы иметь следующую разметку. <ion-nav-view>
— это место, где различные страницы будут загружены позже.
<body ng-app="starter"> <ion-nav-view></ion-nav-view> </body>
Добавление страниц
Откройте файл www / js / app.js и добавьте LocalStorageModule
. Это позволяет нам использовать localStorageService
для хранения и извлечения данных из локального хранилища.
angular.module('starter', ['ionic', 'LocalStorageModule'])
Под методом .run
добавьте конфигурацию приложения, вызвав метод config
. Этот метод принимает функцию обратного вызова, где мы передаем $stateProvider
и $urlRouterProvider
чтобы мы могли указать маршрут для различных страниц в приложении.
.run(function($ionicPlatform) { ... }) .config(function($stateProvider, $urlRouterProvider) { $stateProvider .state('app', { url: '/app', abstract: true, templateUrl: 'templates/menu.html', }) .state('app.login', { url: '/login', views: { 'menuContent': { templateUrl: 'templates/login.html' } } }) .state('app.post-status', { url: '/post-status', views: { 'menuContent': { templateUrl: 'templates/post-status.html' } } }) .state('app.post-photo', { url: '/post-photo', views: { 'menuContent': { templateUrl: 'templates/post-photo.html' } } }) .state('app.send-message', { url: '/send-message', views: { 'menuContent': { templateUrl: 'templates/send-message.html' } } }) .state('app.user-details', { url: '/user-details', views: { 'menuContent': { templateUrl: 'templates/user-details.html' } } }); $urlRouterProvider.otherwise('/app/login'); });
Взломать код Сначала у нас есть абстрактное состояние, называемое app
. Это похоже на родительское состояние, где все остальные состояния, определенные ниже, наследуются от него. В этом случае мы устанавливаем templateUrl
в templates/menu.html
который является путем к основному шаблону, в котором будут наследоваться все представления.
.state('app', { url: '/app', abstract: true, templateUrl: 'templates/menu.html', })
Создайте основной шаблон в каталоге www / templates и назовите его menu.html , добавив следующее.
<ion-side-menus> <ion-side-menu-content> <ion-nav-bar class="bar-positive nav-title-slide-ios7"> <ion-nav-back-button class="button-clear"><i class="icon ion-ios7-arrow-back"></i> Back</ion-nav-back-button> </ion-nav-bar> <ion-nav-view name="menuContent" animation="slide-left-right"></ion-nav-view> </ion-side-menu-content> <ion-side-menu side="left"> <header class="bar bar-header bar-positive"></header> <ion-content class="has-header"> <ion-list> <ion-item nav-clear menu-close href="#/app/login"> login & logout </ion-item> <ion-item nav-clear menu-close href="#/app/post-status"> post status </ion-item> <ion-item nav-clear menu-close href="#/app/post-photo"> post photo </ion-item> <ion-item nav-clear menu-close href="#/app/send-message"> send message </ion-item> <ion-item nav-clear menu-close href="#/app/user-details"> user details </ion-item> </ion-list> </ion-content> </ion-side-menu> </ion-side-menus>
Этот шаблон использует <ion-side-menus>
который отображает боковое меню в левой части приложения. По умолчанию это свернуто, которое показывает меню бургера.
<ion-nav-view>
отображает текущий вид. Атрибут name
должен быть указан, потому что это то место, к которому привязано представление.
<ion-nav-view name="menuContent" animation="slide-left-right"></ion-nav-view>
Основное содержание этого шаблона представляет собой список пунктов меню, которые ссылаются на различные страницы в приложении.
<ion-content class="has-header"> <ion-list> <ion-item nav-clear menu-close href="#/app/login"> login & logout </ion-item> <ion-item nav-clear menu-close href="#/app/post-status"> post status </ion-item> <ion-item nav-clear menu-close href="#/app/post-photo"> post photo </ion-item> <ion-item nav-clear menu-close href="#/app/send-message"> send message </ion-item> <ion-item nav-clear menu-close href="#/app/user-details"> user details </ion-item> </ion-list> </ion-content>
Возвращаясь к файлу www / js / app.js , мы затем определяем состояния для разных страниц. Все эти состояния используют объявленное ранее абстрактное состояние. На это указывает префикс каждого маршрута с app.
, url
— это URL-адрес, по которому отображается представление. Для маршрута входа в систему мы указали только /login
. Но поскольку мы используем абстрактное состояние, фактическим URL-адресом будет /app/login
поскольку значением для url
назначенным ранее абстрактному состоянию, является /app
. Затем у нас есть объект views
в котором мы указываем имя представления, в котором отображается шаблон. Для этого требуется свойство templateUrl
которое содержит путь к шаблону. Тот же шаблон используется во всех других маршрутах.
.state('app.login', { url: '/login', views: { 'menuContent': { templateUrl: 'templates/login.html' } } }) .state('app.post-status', { url: '/post-status', views: { 'menuContent': { templateUrl: 'templates/post-status.html' } } }) .state('app.post-photo', { url: '/post-photo', views: { 'menuContent': { templateUrl: 'templates/post-photo.html' } } }) .state('app.send-message', { url: '/send-message', views: { 'menuContent': { templateUrl: 'templates/send-message.html' } } }) .state('app.user-details', { url: '/user-details', views: { 'menuContent': { templateUrl: 'templates/user-details.html' } } });
Наконец, мы указываем страницу по умолчанию:
$urlRouterProvider.otherwise('/app/login');
Сервисы
Мы используем сервисы в качестве контейнера для задач, необходимых для выполнения более одного раза по всему приложению. Таким образом, мы можем избежать повторения одного и того же кода.
Камера Сервис
Первый сервис, который является CameraService
, служит контейнером для вызовов API, которые мы можем сделать с помощью плагина Camera. Создайте файл CameraService.js в каталоге js / controllers и добавьте следующее.
(function(){ angular.module('starter') .service('CameraService', ['$q', CameraService]); function CameraService($q){ var me = this; me.options = { quality: 80, targetWidth: 300, targetHeight: 300, correctOrientation: true }; function getPicture(){ var q = $q.defer(); navigator.camera.getPicture( function(result){ q.resolve(result); }, function(err){ q.reject(err); }, me.options ); return q.promise; } return { getPicture: getPicture } } })();
Разбивая код, мы сначала оборачиваем все в «выражение немедленно выполняемой функции». Это предотвращает конфликт с другими скриптами.
(function(){ ... })();
Далее мы указываем модуль, к которому принадлежит этот сервис, и что этот сервис зависит от сервиса Angular $q
. Это позволяет нам запускать функции асинхронно.
angular.module('starter') .service('CameraService', ['$q', CameraService]);
Затем это передается в качестве аргумента функции CameraService
.
function CameraService($q){ }
Внутри функции мы устанавливаем переменную me
в качестве псевдонима для текущего контекста и используем ее для установки параметров плагина камеры.
var me = this; me.options = { quality: 80, targetWidth: 300, targetHeight: 300, correctOrientation: true };
Далее у нас есть функция getPicture
. Эта функция вызывается каждый раз, когда нам нужно сделать фотографию. Это возвращает обещание, которое означает, что мы можем использовать метод then
чтобы передать функцию, которую мы хотим выполнить, как только пользователь выберет фотографию.
function getPicture(){ var q = $q.defer(); navigator.camera.getPicture( function(result){ q.resolve(result); }, function(err){ q.reject(err); }, me.options ); return q.promise; } return { getPicture: getPicture }
Служба запросов
Служба запросов отправляет HTTP-запросы на сервер приложений. Приложение, которое мы создаем, имеет серверный компонент, который позволяет нам проверять ответ, возвращаемый API Facebook, и загружать фотографии на сервер. Создайте файл RequestService.js в каталоге js / services и добавьте следующее:
(function(){ angular.module('starter') .service('RequestsService', ['$http', '$q', '$ionicLoading', '$timeout', '$ionicPopup', RequestsService]); function RequestsService($http, $q, $ionicLoading, $timeout, $ionicPopup){ var base_url = 'http://YOUR-SERVER-URL'; var me = this; me.timeout = { value: 20000, message: 'Please check your internet connection and re-launch the app' }; function requestTimeout(deferred){ var timer = $timeout(function(){ $ionicLoading.hide(); $ionicPopup.alert({ 'title': me.timeout.message }); deferred.reject(); }, me.timeout.value); return timer; }; function sendData(data){ var deferred = $q.defer(); var timer = requestTimeout(deferred); $ionicLoading.show(); $http.post(base_url + '/data', {'data' : data}) .success(function(response){ $timeout.cancel(timer); $ionicLoading.hide(); $ionicPopup.alert({ 'title': response.message }); deferred.resolve(response); }) .error(function(data){ deferred.reject(); }); return deferred.promise; }; function uploadPhoto(photo_url, params){ var deferred = $q.defer(); var options = new FileUploadOptions(); options.fileKey = 'file'; options.fileName = photo_url.substr(photo_url.lastIndexOf('/') + 1).split('?')[0]; options.mimeType = 'image/jpeg'; options.params = params; var ft = new FileTransfer(); ft.upload( photo_url, base_url + '/upload', function(result){ deferred.resolve(result); }, function(err){ deferred.reject(err); }, options ); return deferred.promise; } return { sendData: sendData, uploadPhoto: uploadPhoto }; } })();
Разбивая код, мы сначала импортируем несколько сервисов, встроенных в Angular и Ionic.
angular.module('starter') .service('RequestsService', ['$http', '$q', '$ionicLoading', '$timeout', '$ionicPopup', RequestsService]);
-
$http
: Позволяет нам делать HTTP-запросы. -
$ionicLoading
: показывает загрузчик gif каждый раз, когда мы делаем HTTP-запросы. -
$timeout
: способ реализацииsetTimeout
Angular. -
$ionicPopup
: версия окна оповещения Ionic.
Установите настройки для базового URL для отправки запросов и тайм-аутов
var base_url = 'http://YOUR-SERVER-URL'; var me = this; me.timeout = { value: 20000, message: 'Please check your internet connection and re-launch the app' };
Функция requestTimeout
позволяет нам предупреждать пользователя, когда запрос достигает заданного значения времени ожидания. Это работает, останавливая время ожидания, как только мы получим ответ от запроса.
function requestTimeout(deferred){ var timer = $timeout(function(){ $ionicLoading.hide(); $ionicPopup.alert({ 'title': me.timeout.message }); deferred.reject(); }, me.timeout.value); return timer; };
Функция sendData
позволяет нам отправлять данные на сервер. Для этого приложения мы используем его для отправки пользовательских данных на сервер, а затем сохранить их в базе данных. Эта функция принимает данные, отправленные в качестве своего параметра, а затем использует службу $http
Angular для отправки запроса POST
на сервер. Как только мы получаем успех в качестве ответа, мы отменяем тайм-аут, чтобы не вызывать вызов $ionicPopup.alert
. Как и с другими функциями, мы используем сервис $q
чтобы превратить его в асинхронный вызов функции. Позже, когда мы начнем вызывать эти функции из контроллера, вы увидите множество вызовов метода then()
запускаемых всякий раз, когда мы вызываем deferred.resolve(response)
. Затем мы можем передать функцию в метод then()
в котором мы можем получить доступ к response
возвращенному из запроса.
function sendData(data){ var deferred = $q.defer(); var timer = requestTimeout(deferred); $ionicLoading.show(); $http.post(base_url + '/data', {'data' : data}) .success(function(response){ $timeout.cancel(timer); $ionicLoading.hide(); $ionicPopup.alert({ 'title': response.message }); deferred.resolve(response); }) .error(function(data){ deferred.reject(); }); return deferred.promise; };
Функция uploadPhoto
позволяет нам загружать фотографии на сервер. Это принимает photo_url
который в основном является FILE_URI
возвращаемым плагином камеры после того, как фотография была сделана. params
содержит любые пользовательские данные, которые мы хотим передать в отношении файла.
function uploadPhoto(photo_url, params){ var deferred = $q.defer(); var options = new FileUploadOptions(); options.fileKey = 'file'; options.fileName = photo_url.substr(photo_url.lastIndexOf('/') + 1).split('?')[0]; options.mimeType = 'image/jpeg'; options.params = params; var ft = new FileTransfer(); ft.upload( photo_url, me.upload_url + '/upload', function(result){ deferred.resolve(result); }, function(err){ deferred.reject(err); }, options ); return deferred.promise; };
Контроллеры
Контроллеры в основном используются для прослушивания событий и реагирования на них. Пример — когда пользователь нажимает кнопку. Контроллер отвечает за обработку этого конкретного события.
Авторизоваться
Контроллер входа обрабатывает все события, которые происходят на странице входа в приложение. Создайте файл LoginController.js в каталоге js / controllers и добавьте следующее:
(function(){ angular.module('starter') .controller('LoginController', ['$scope', 'localStorageService', 'RequestsService', LoginController]); function LoginController($scope, localStorageService, RequestsService){ var me = this; me.updateLoginStatus = function(){ facebookConnectPlugin.getLoginStatus( function(response){ if(response.status === 'connected'){ me.logged_in = true; }else{ me.logged_in = false; } }, function(err){ me.logged_in = false; alert('Error while trying to check login status'); RequestsService.sendData(err); } ); }; $scope.fbLogin = function(){ facebookConnectPlugin.login(['email'], function(response){ me.logged_in = true; alert('logged in successfully'); alert(JSON.stringify(response.authResponse)); RequestsService.sendData(response.authResponse); localStorageService.set('user.id', response.authResponse.userID); localStorageService.set('user.access_token', response.authResponse.accessToken); }, function(err){ RequestsService.sendData(err); alert('an error occured while trying to login. please try again.'); }); }; $scope.fbLogout = function(){ facebookConnectPlugin.logout( function(response){ alert(JSON.stringify(response)); RequestsService.sendData(response); }, function(err){ alert(JSON.stringify(err)); RequestsService.sendData(err); } ); }; } })();
Как и сервисы, мы можем импортировать сервисы внутри контроллеров. На этот раз мы используем два новых сервиса: $scope
и localStorageService
.
angular.module('starter') .controller('LoginController', ['$scope', 'localStorageService', 'RequestsService', LoginController]);
Вот краткое описание того, что они делают:
-
$scope
: используется для присоединения данных или событий к текущей странице. -
localStorageService
: используется для сохранения и извлечения данных из локального хранилища.
Внутри контроллера мы прикрепляем функцию updateLoginStatus
. Эта функция проверяет, активен ли текущий сеанс Facebook через объект facebookConnectPlugin
доступный глобально из плагина Facebook. Затем мы обновляем значение свойства logged_in
на основе результата. Это переключает переключатель в представлении о том, отображать ли кнопку входа или выхода из системы.
me.updateLoginStatus = function(){ facebookConnectPlugin.getLoginStatus( function(response){ if(response.status === 'connected'){ me.logged_in = true; }else{ me.logged_in = false; } }, function(err){ me.logged_in = false; alert('Error while trying to check login status'); RequestsService.sendData(err); } ); };
Присоедините функцию fbLogin
к текущей области. Это выполняется, когда пользователь нажимает кнопку входа в систему .
$scope.fbLogin = function(){ ... }
Внутри функции fbLogin
мы вызываем метод login
из объекта facebookConnectPlugin
, открывая окно входа в Facebook. Если приложение Facebook установлено и пользователь в настоящий момент вошел в систему, все, что ему нужно сделать, это согласиться с разрешениями приложения для аутентификации приложения. В этом случае разрешение передается по email
. Это означает, что приложение будет иметь доступ к адресу электронной почты пользователя. Как только пользователь соглашается с разрешениями, вызывается функция обратного вызова успеха, в противном случае вызывается функция обратного вызова ошибки. Когда пользователь соглашается, ответ содержит пользовательские данные. Мы используем localStorageService
чтобы сохранить их в локальном хранилище, а затем используем RequestsService
чтобы отправить его на сервер.
facebookConnectPlugin.login(['email'], function(response){ me.logged_in = true; alert('logged in successfully'); alert(JSON.stringify(response.authResponse)); localStorageService.set('user.id', response.authResponse.userID); localStorageService.set('user.access_token', response.authResponse.accessToken); RequestsService.sendData(response.authResponse); }, function(err){ RequestsService.sendData(err); alert('an error occured while trying to login. please try again.'); });
Вот как будет выглядеть логин:
Функция fbLogout
используется для выхода из Facebook. Это разрушает текущий сеанс пользователя.
$scope.fbLogout = function(){ facebookConnectPlugin.logout( function(response){ me.logged_in = false; alert(JSON.stringify(response)); RequestsService.sendData(response); }, function(err){ alert(JSON.stringify(err)); RequestsService.sendData(err); } ); };
Теперь мы можем добавить вид входа в систему. Представления сохраняются в каталоге шаблонов . Создайте файл login.html внутри этого каталога и добавьте следующее.
<ion-view title="Login & Logout" ng-controller="LoginController as login_ctrl" ng-init="login_ctrl.updateLoginStatus()"> <ion-nav-buttons side="left"> <button menu-toggle="left" class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content class="has-header padding"> <button class="button button-positive button-block" ng-hide="login_ctrl.logged_in" ng-click="fbLogin()"> Login with Facebook </button> <button class="button button-assertive button-block" ng-show="login_ctrl.logged_in" ng-click="fbLogout()"> Logout </button> </ion-content> </ion-view>
Все представления начинаются с <ion-view>
. В приведенном выше коде мы передаем заголовок (показанный в заголовке страницы) и контроллер, который использует это представление. Директива ng-init
выполняет updateLoginStatus
после инициализации этого представления. Это означает, что он сразу же запускается, когда пользователь переходит на страницу входа.
<ion-content>
определяет содержимое страницы. В этом случае все, что нам нужно, это кнопка для входа в Facebook. Мы добавили к этой кнопке атрибут ng-click
и в качестве значения fbLogin
функцию fbLogin
определенную ранее в loginController
. ng-click
— это встроенная в Angular директива, которая в основном используется для прослушивания событий щелчка в определенном элементе. Это означает, что когда кнопка нажата, она выполняет функцию fbLogin
, то же самое верно и для кнопки, используемой для выхода из Facebook. Директивы ng-hide
и ng-show
скрывают и отображают эти две кнопки в зависимости от того, вошел ли пользователь в систему или нет.
Данные пользователя
UserDetailsController
отображает информацию о зарегистрированном пользователе. Создайте файл UserDetailsController.js в каталоге www / js и добавьте следующее.
(function(){ angular.module('starter') .controller('UserDetailsController', ['$scope', 'localStorageService', 'RequestsService', UserDetailsController]); function UserDetailsController($scope, localStorageService, RequestsService){ var me = this; $scope.user = null; me.getUserInfo = function(){ var user_id = localStorageService.get('user.id'); facebookConnectPlugin.api( user_id + "/?fields=id,email,first_name,last_name,gender,age_range", ['public_profile', 'email'], function (response) { alert(JSON.stringify(response)); RequestsService.sendData(response); $scope.user = response; }, function (error) { alert("Failed: " + error); } ); }; } })();
Внутри контроллера мы устанавливаем для user
значение null
поэтому информация о пользователе отображается только при нажатии кнопки, вызывая функцию getUserInfo
.
me.getUserInfo = function(){ ... }
Внутри функции мы получаем идентификатор пользователя Facebook из локального хранилища.
var user_id = localStorageService.get('user.id');
И используйте его для получения информации о пользователе от Graph API. Мы только пытаемся получить основную информацию без регистрации приложения через API.
Чтобы сделать запрос к API Graph, вызовите метод api
и передайте четыре аргумента. Первый путь, по которому сделан запрос. Поскольку мы работаем с пользовательскими данными, мы используем идентификатор Facebook пользователя в качестве базы и указываем, какую информацию мы хотим получить, предоставляя fields
в качестве параметра запроса.
Затем мы передаем разделенный запятыми список всех полей, которые мы хотим получить. Если вы хотите получить полный список доступных полей, ознакомьтесь с Справочником пользователя API Graph Facebook .
Второй аргумент — это массив, содержащий различные разрешения, которые пользователь должен утвердить. Здесь нам требуются пользователи public_profile
и email
. Вы можете увидеть полный список разрешений на странице справки по разрешениям . Обратите внимание, что если в конкретном разрешении указано, что для его просмотра требуется Facebook, вы не сможете его использовать, даже если вы разработчик приложения.
Третий и четвертый аргументы — это обратные вызовы об успехе и ошибке Если мы добьемся успеха, мы выдадим предупреждение, чтобы увидеть ответ, отправить его на сервер и назначить ответ переменной user
. В представлении сведений о пользователе эта user
переменная проверяется на наличие и, если она существует, отображает пользовательские данные.
facebookConnectPlugin.api( user_id + "/?fields=id,email,first_name,last_name,gender,age_range", ['public_profile', 'email'], function (response) { alert(JSON.stringify(response)); RequestsService.sendData(response); $scope.user = response; }, function (error) { alert("Failed: " + error); } );
Вот представление сведений о пользователе ( www / templates / user-details.html ). Вы можете видеть, что мы использовали директиву ng-if
чтобы проверить, установлена ли переменная user
. Если это так, то отображаются данные пользователя.
<ion-view title="User Details" ng-controller="UserDetailsController as details_ctrl"> <ion-nav-buttons side="left"> <button menu-toggle="left" class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content class="has-header padding"> <button class="button button-positive" ng-hide="user" ng-click="details_ctrl.getUserInfo()"> Show User Details </button> <div class="card"> <div class="item item-text-wrap" ng-if="user"> <ul> <li>id: {{ user.id }}</li> <li>email: {{ user.email }}</li> <li>name: {{ user.first_name }} {{ user.last_name }}</li> <li>gender: {{ user.gender }}</li> <li>age_range: {{ user.age_range.min }}</li> </ul> </div> </div> </ion-content> </ion-view>
Вот как должна выглядеть страница с информацией о пользователе:
диалог
DialogController
обрабатывает события на страницах, которые используют диалоги Facebook, такие как диалог подачи, отправки и обмена. Создайте DialogController.js в каталоге js / controllers и добавьте следующее.
(function(){ angular.module('starter') .controller('DialogController', ['$scope', 'RequestsService', 'CameraService', DialogController]); function DialogController($scope, RequestsService, CameraService){ var me = this; me.base_uploads_url = 'YOUR-SERVER-URL/uploads'; $scope.postStatus = function(){ var dialog_options = { method: 'feed', link: me.url, caption: me.caption }; facebookConnectPlugin.showDialog(dialog_options, function(response){ alert('posted!'); RequestsService.sendData(response); }, function(err){ RequestsService.sendData(err); alert('something went wrong while trying to post'); }); }; $scope.capturePhoto = function(){ CameraService.getPicture().then(function(imageURI) { alert(imageURI); me.photo = imageURI; }, function(err) { alert(err); }); }; $scope.postPhoto = function(){ var dialog_options = { method: "feed", name: me.caption, message: me.caption, caption: me.caption, description: me.caption }; var photo_data = { 'caption': me.caption }; RequestsService.uploadPhoto(me.photo, photo_data).then(function(response){ var res = JSON.parse(response.response); dialog_options.picture = me.base_uploads_url + res.image_url; facebookConnectPlugin.showDialog(dialog_options, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) } ); }, function(response){ alert(JSON.stringify(response)); }); }; $scope.sendMessage = function(){ facebookConnectPlugin.showDialog( { method: "send", link: me.url }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) } ); }; } })();
Разбивая код, внутри контроллера находится URL для загрузки фотографий. Именно здесь RequestsService
отправляет захваченное фото.
me.base_uploads_url = 'YOUR-SERVER-URL/uploads/';
Далее у нас postStatus
функция postStatus
когда пользователь нажимает кнопку для публикации статуса Facebook.
$scope.postStatus = function(){ ... }
Вместо того, чтобы публиковать сообщения напрямую с помощью API Graph, мы используем диалог подачи в Facebook Для этого требуется объект, содержащий тип диалога, URL-адрес для включения в сообщение и текст для отображения в качестве заголовка.
var dialog_options = { method: 'feed', //type of dialog link: me.url, //URL to include in the post caption: me.caption //the text which will show as the title of the link };
Вызовите метод showDialog
и передайте dialog_options
в качестве первого аргумента. Второй и третий аргументы — это обратные вызовы об успехе и ошибке. Обратный вызов успеха выполняется, если пользователь действительно публикует сообщение. Обратный вызов ошибки выполняется, если пользователь отменяет.
facebookConnectPlugin.showDialog(dialog_options, function(response){ alert('posted!'); RequestsService.sendData(response); }, function(err){ RequestsService.sendData(err); alert('something went wrong while trying to post'); });
Вот как должна выглядеть публикация статуса в приложении:
Присоедините функцию для открытия приложения камеры по умолчанию на устройстве к области $scope
. Это использует CameraService
для запуска приложения камеры для открытия. Как только пользователь заканчивает фотографировать, он назначает локальный путь к photo
свойству photo
контроллера. Это отображает фактическое изображение. Позже это значение используется функцией для публикации фотографии.
$scope.capturePhoto = function(){ CameraService.getPicture().then(function(imageURI) { alert(imageURI); me.photo = imageURI; }, function(err) { alert(err); }); };
Далее идет способ размещения фотографии.
$scope.postPhoto = function(){ ... }
Внутри мы добавляем параметры для диалога Facebook. Мы снова используем диалог подачи, но на этот раз добавляем другие параметры, такие как name
(имя вложения ссылки), caption
(текст, который отображается под именем ссылки) и description
(отображается под текстом заголовка).
var dialog_options = { method: "feed", name: me.caption, caption: me.caption, description: me.caption };
Создайте объект, в котором хранятся данные, представленные вместе с фотографией. В этом случае нам нужна только подпись, введенная пользователем.
var photo_data = { 'caption': me.caption };
Сделайте HTTP-запрос на загрузку фотографии на сервер. Обратите внимание, что это сервер, используемый приложением для загрузки фотографий и проверки ответов, а не серверы Facebook. Это потому, что мы используем диалог фида Facebook, который не может напрямую принимать загрузки. Все, что он может сделать, это принять URL-адрес изображения и добавить его в качестве ссылки на сообщение. Это означает, что мы просто ссылаемся на изображение. Сервер возвращает имя файла для загруженной фотографии, и мы используем его в качестве значения для атрибута picture
в диалоговом окне.
RequestsService.uploadPhoto(me.photo, photo_data).then(function(response){ var res = JSON.parse(response.response); dialog_options.picture = me.base_uploads_url + res.image_url; ... }, function(response){ alert(JSON.stringify(response)); });
Получив это, мы вызываем showDialog
чтобы открыть другое диалоговое окно Facebook, которое ссылается на загруженную фотографию
facebookConnectPlugin.showDialog(dialog_options, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) } );
Вот как выглядит размещение фотографии:
Наконец, для DialogController
у нас есть метод sendMessage
который открывает диалог отправки Facebook и мы передаем URL-адрес, введенный пользователем. Диалог отправки затем создает предварительный просмотр для этого URL и позволяет пользователю выбрать, кому его отправлять, и необязательное текстовое сообщение.
$scope.sendMessage = function(){ facebookConnectPlugin.showDialog( { method: "send", link: me.url }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) }, function (response) { RequestsService.sendData(response); alert(JSON.stringify(response)) } ); };
Вот как должна выглядеть отправка сообщений:
DialogController
используется в этих трех представлениях:
— просмотр статуса публикации
— вид для размещения фотографии
— вид для отправки сообщения
Представление для статуса публикации ( www / templates / post-status.html ) принимает значения для URL-адреса и заголовка статуса для публикации. При нажатии на кнопку « Статус сообщения» открывается диалоговое окно фида Facebook.
<ion-view title="Post Status" ng-controller="DialogController as dialog_ctrl"> <ion-nav-buttons side="left"> <button menu-toggle="left" class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content class="has-header padding"> <div class="list"> <label class="item item-input"> <input type="url" ng-model="dialog_ctrl.url" placeholder="URL"> </label> <label class="item item-input"> <input type="text" ng-model="dialog_ctrl.caption" placeholder="Caption"> </label> </div> <button class="button button-positive button-block" ng-click="postStatus()"> Post Status </button> </ion-content> </ion-view>
Представление для отправки сообщения ( www / templates / send-message.html ) принимает URL-адрес, которым пользователь хочет поделиться. Нажатие на кнопку « Отправить сообщение» открывает диалоговое окно отправки в Facebook.
<ion-view title="Send Message" ng-controller="DialogController as dialog_ctrl"> <ion-nav-buttons side="left"> <button menu-toggle="left" class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content class="has-header padding"> <div class="list"> <label class="item item-input"> <input type="url" ng-model="dialog_ctrl.url" placeholder="URL"> </label> </div> <button class="button button-balanced button-block" ng-click="sendMessage()"> Send Message </button> </ion-content> </ion-view>
Представление для публикации фотографий ( www / templates / post-photo.html ) содержит кнопку для захвата фотографий. Как мы видели в DialogController
, это открывает приложение камеры по умолчанию на устройстве. После того, как фотография была сделана, она отображается внутри #photo-container
div вместе с текстовым полем, которое запрашивает у пользователя подпись. При нажатии на кнопку «Фотография» открывается диалоговое окно канала, в котором отображается предварительный просмотр сообщения.
<ion-view title="Post Photo" ng-controller="DialogController as dialog_ctrl"> <ion-nav-buttons side="left"> <button menu-toggle="left" class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content class="has-header padding"> <button class="button button-balanced button-block" ng-click="capturePhoto()"> Capture Photo </button> <div id="photo-container" ng-if="dialog_ctrl.photo"> <div class="card"> <img ng-src="{{ dialog_ctrl.photo }}"> </div> <div class="list"> <label class="item item-input"> <input type="text" ng-model="dialog_ctrl.caption" placeholder="Caption"> </label> </div> <button class="button button-balanced button-block" ng-click="postPhoto()"> Post Photo </button> </div> </ion-content> </ion-view>
Добавление серверного компонента
На протяжении всего приложения мы делали запросы к серверу, но еще не создали его. В этом разделе я создам серверный компонент приложения, чтобы мы могли его завершить.
Прежде чем мы продолжим работу с кодом, нам нужно установить следующие зависимости:
- Express : веб-фреймворк для Node.js.
- body-parser : используется для анализа тела запроса, используется всякий раз, когда мы отправляем данные на сервер. Данные, которые мы отправляем, анализируются этой библиотекой, чтобы мы могли их использовать.
- multer : Используется для обработки загрузки файлов.
Чтобы установить зависимости, создайте папку с именем server в корневом каталоге приложения. Здесь мы сохраним файлы, используемые сервером. Внутри папки создайте файл package.json и добавьте следующее:
{ "name": "cordova-social", "version": "0.0.1", "dependencies": { "body-parser": "^1.14.1", "express": "^4.13.3", "multer": "^1.1.0" } }
Сохраните файл и выполните npm install
установку, чтобы установить зависимости.
Создайте файл app-server.js и добавьте следующее:
var express = require('express'); var app = express(); var multer = require('multer'); var upload = multer({ dest: 'public/uploads/' }); var bodyParser = require('body-parser'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(express.static('public')); var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); }); app.get('/', function (req, res) { res.send('Hello World!'); }); app.post('/data', function(req, res){ console.log('received request'); console.log(req.body); res.send('ok'); }); app.post('/upload', upload.single('file'), function(req, res){ console.log('received upload request'); console.log(req.body); console.log(req.file); res.send({'image_url': req.file.filename}); });
Здесь мы сначала импортируем все зависимости и устанавливаем их параметры по умолчанию. Поскольку multer
мы устанавливаем папку для загрузки в public / uploads . Создайте эту папку и установите необходимые разрешения, например:
sudo chmod -R 777 public
var express = require('express'); var app = express(); var multer = require('multer'); var upload = multer({ dest: 'public/uploads/' });
Установите приложение для использования body-parser
библиотеки.
var bodyParser = require('body-parser'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded());
Установите общую папку как статический каталог для экспресс. Если файл с именем myphoto.png загружен в каталог public / uploads , он доступен по следующему URL-адресу: http://your-server.com/uploads/myphoto.png
app.use(express.static('public'));
Присоедините приложение к порту 3000
. Это позволяет получить доступ по адресу http: // localhost: 3000 .
var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); });
Определите маршрут для домашней страницы. Мы используем его только для тестирования, чтобы вы могли проверить, работает ли сервер.
app.get('/', function (req, res) { res.send('Hello World!'); });
Определите маршрут для приема данных, представленных из приложения. Маршрут регистрирует, что запрос был получен, и содержимое тела запроса. Затем он возвращает «хорошо» в качестве ответа.
app.post('/data', function(req, res){ console.log('received request'); console.log(req.body); res.send('ok'); });
Наконец, у нас есть маршрут для приема загруженных файлов. При этом используется upload
объект, предоставленный multer
библиотекой, для загрузки одного файла. Как только файл загружен, данные req.file
объекта становятся доступны в объекте, и мы отправляем имя файла обратно в приложение.
app.post('/upload', upload.single('file'), function(req, res){ console.log('received upload request'); console.log(req.body); console.log(req.file); res.send({'image_url': req.file.filename}); });
Развертывание и запуск приложения
Теперь мы готовы скомпилировать приложение и развернуть его на устройстве Android. Прежде чем мы это сделаем, нам нужно запустить сервер узлов и сделать его доступным через Интернет.
node app-server.js
Используйте ngrok, чтобы открыть его в Интернете:
ngrok http 3000
Это возвращает URL, который вы можете использовать в приложении. Откройте js / services / RequestsService.js и обновите base_url
. В js / controllers / DialogController.js обновить base_uploads_url
.
Скомпилируйте и запустите приложение для Android:
cordova build android
Вывод
Это оно!Из этого урока вы узнали, как работать с API Facebook в приложении Cordova. В частности, вы узнали, как входить и выходить из Facebook, используя Graph API для получения данных о пользователях и диалоги Facebook для публикации статуса и отправки сообщений. Вы можете получить доступ к исходному коду, используемому в этом руководстве, в этом репозитории Github, и я хотел бы услышать любые ваши комментарии или вопросы.