По результатам опроса из первого топика продолжаю серию статей об Ionic Framework (далее IF). Сегодня поговорим о работе с камерой устройства и в дополнение рассмотрим работу с localStorage. В качестве основы возьмем приложение из прошлого топика и добавим нужную функциональность.
- Добавление ngCordova
- Подключение плагина для работы с камерой
- Создание сервиса для работы с камерой
- Редактирование шаблонов
- Добавление маршрутов, контроллеров и фабрик
- Заключение
Добавление ngCordova
Для работы установленных плагинов необходимо добавить модуль ngCordova. Ставится он просто (для начала обновим наши инструменты, ведь прошло много времени с момента публикации первой статьи…):
sudo npm i -g cordova ionic bower bower install ngCordova
Далее ngCordova подключается в index.html:
<script src="lib/ngCordova/dist/ng-cordova.min.js"></script>
и в зависимости приложения (app.js):
angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services'])
иначе паровозик не сможет… Желательно также подключить еще пару плагинов для работы с ФС устройства:
ionic plugin add cordova-plugin-file ionic plugin add cordova-plugin-file-transfer
По идее можно было бы подключить только file-transfer, т.к. вместе с собой он также тянет и file, но иногда такой способ чреват появлением непонятных багов…
Подключение плагина для работы с камерой
Для того чтобы IF смог получить доступ к камере устройства нужно добавить в проект Cordova плагин реализующий этот функционал:
ionic plugin add cordova-plugin-camera
Плагин предоставляет единственный метод:
navigator.camera.getPicture(SuccessCallback, ErrorCallback, Options);
Где SuccessCallback и ErrorCallback это наши функции-обработчики в которых мы вольны писать все что вздумается, а вот объект Options гораздо интереснее для рассмотрения:
- quality (Number): Качество сохраняемого изображения, в интервале от 0 до 100, где 100 — режим без сжатия. По умолчанию значение 50.
- destinationType(Number): Формат возвращаемого значения. По умолчанию FILEURI. Настройка определяется типами navigator.camera.DestinationType:
Camera.DestinationType = { DATA_URL : 0, // Строка в формате base64 FILE_URI : 1, // URI файла NATIVE_URI : 2 // URI пути зависящий от платформы (assets-library:// для iOS и content:// для Android) };
- sourceType (Number): Источник изображения. По умолчанию CAMERA. Настройка определяется типами navigator.camera.PictureSourceType:
Camera.PictureSourceType = { PHOTOLIBRARY : 0, CAMERA : 1, SAVEDPHOTOALBUM : 2 };
- allowEdit (Boolean): Включает режим простого редактирования перед передачей в функцию.
- encodingType (Number): Формат файла. По умолчанию «у вас JPEG». Настройка определяется типами navigator.camera.EncodingType:
Camera.EncodingType = { JPEG : 0, // JPEG PNG : 1 // PNG };
- targetWidth (Number): Ширина. Без комментариев. Для масштабирования. Лучше использовать в связке с targetHeight с коэффициентом.
- targetHeight (Number): Высота.
- mediaType (Number): Тип мультимедиа контента. По умолчанию PICTURE. Работает при PictureSourceType равным PHOTOLIBRARY или SAVEDPHOTOALBUM. Настройка определяется типами nagivator.camera.MediaType:
Camera.MediaType = { PICTURE: 0, // Только изображения. VIDEO: 1, // Только видео (ВСЕГДА ВОЗВРАЩАЕТ FILE_URI) ALLMEDIA : 2 // Все типы };
- correctOrientation (Boolean): Поворот изображения для компенсации ориентации устройства во время съемки.
- saveToPhotoAlbum (Boolean): Сохранение в альбом после съемки.
- popoverOptions: iOS-only параметр определяющий местонахождение popover на iPad. Настройка определяется в CameraPopoverOptions.
- cameraDirection (Number): Камера для использования (фронтальная или задняя). По умолчанию BACK. Настройка определяется типами navigator.camera.Direction:
Camera.Direction = { BACK : 0, // Use the back-facing camera FRONT : 1 // Use the front-facing camera };
Вот в принципе и все что можно рассказать…
Создание сервиса для работы с камерой
Давайте для нашего удобства сделаем службу чтобы работа с камерой была комфортной. Для этого предлагаю реализовать фабрику с объектом promise который будет обрабатывать события камеры с помощью наших callback’ов. Откройте файл services.js (в прошлом топике мы уже сделали там фабрику городов) и приведите его к такому виду:
angular.module('starter.services', []) .factory('Cities', function() { // ... }).factory('Cam', function($q) { return { getPic: function(opt) { var q = $q.defer(); navigator.camera.getPicture(function(res) { q.resolve(res); }, function(err) { q.reject(err); }, opt); return q.promise; } }; });
Итак, в этой настройке мы, используя Angular’овский $q.defer(), возвращаем promise, которому впоследствии передадим обработчики событий с камеры (SuccessCallback, ErrorCallback) и объект настройки (Options). Не закрывайте файл, мы к нему еще вернемся!
Редактирование шаблонов
Откройте файл tabs.html и добавьте еще один tab. У меня получился такой:
<ion-tab title="Options" icon-off="ion-gear-a" icon-on="ion-gear-a" href="#/tab/opt"> <ion-nav-view name="tab-opt"></ion-nav-view> </ion-tab>
Теперь в папке с шаблонами создадим еще один шаблон (например tab-opt.html) и сделаем небольшую форму настройки пользователя приложения, например такую:
<ion-view view-title="Настройки"> <ion-content class="padding"> <div class="list-item"> <img class="full-image" ng-src="{{user.ava || './img/ionic.png'}}"> <label class="item item-input"> <i class="icon ion-person placeholder-icon"></i> <input type="text" placeholder="Имя пользователя" ng-model="user.name" ng-keyup="setUser();" /> </label> <label class="item item-input item-select"> <div class="input-label"> Город </div> <select ng-model="user.city" ng-change="setUser();"> <option ng-repeat="city in cities" ng-value="city.id">{{city.name}}</option> </select> </label> <button class="button button-full button-positive" ng-click="getPic(1);"> С камеры </button> <button class="button button-full button-calm" ng-click="getPic(0);"> Из библиотеки </button> </div> </ion-content> </ion-view>
Как видно, к полям мы привязали модель пользователя и при изменении значения поля html, в модели будет изменяться соответствующее поле объекта. Так же на события когда клавиша отпущена (кстати, тут можно использовать debounce) и на изменение выбора города, была сделана привязка обработчика setUser, это все мы опишем на следующем шаге. Не терпится запустить и посмотреть? Но мы еще не настроили маршрутизацию, контроллеры и работу с localStorage, так что придется еще немного подождать…
Добавление маршрутов, контроллеров и фабрик
Займемся для начала маршрутами. Откройте app.js и добавьте маршрутизацию для нашего нового таба:
.state('tab.opt', { url: '/opt', views: { 'tab-opt': { templateUrl: 'templates/tab-opt.html', controller: 'OptCtrl' } } })
Отлично, теперь перейдем в controllers.js и добавим нужный контроллер:
.controller('OptCtrl', function($scope, $ionicPopup, Cities, Cam, LS) { $scope.cities = Cities.all(); $scope.user = LS.getIt('user') || {}; $scope.showAlert = function(title, text) { $ionicPopup.alert({ title: title, template: text }); }; $scope.setUser = function() { LS.setIt('user', $scope.user); }; $scope.getPic = function(source) { var opt = { sourceType: source }; Cam.getPic(opt).then(function(res) { $scope.user.ava = res; $scope.setUser(); }, function(err) { $scope.showAlert("Ошибка", err); }); } })
Оппа, что за LS в зависимостях контроллера!? Ничего не бойтесь, это всего лишь метод для работы с localStorage, и настало время его реализовать (services.js):
.factory('LS', function() { return { getIt: function(item) { var it = false; try { it = JSON.parse(localStorage.getItem(item)); return it; } catch(e) { console.err(e); return it; } }, setIt: function(item, obj) { try { localStorage.setItem(item, JSON.stringify(obj)); return true; } catch(e) { console.err(e); return false; } } }; })
И надо бы добавить еще городов, добавьте свой:
{ id: 555312, name: 'Иваново', desc: 'Город невест', emblem: 'https://upload.wikimedia.org/wikipedia/commons/8/8f/Coat_of_Arms_of_Ivanovo_oblast.png' }
Вы сейчас наверное думаете «к чему столько телодвижений?». А представьте себе, что пользователь настроил город (автоматически сохранив в localStorage) и при повторном запуске приложения его будет сразу перекидывать на погоду в родном городе, круто же! Финальный штрих, изменим два наших контроллера, написанных в прошлой статье:
.controller('CityCtrl', function($scope, Cities, LS) { $scope.cities = Cities.all(); $scope.user = LS.getIt('user') || {}; if ($scope.user.city) { window.location.href = '#/tab/city/'+$scope.user.city; } }) .controller('CityDetailCtrl', function($scope, $http, $stateParams, $ionicPopup, $ionicLoading) { $scope.data = {}; $scope.id = $stateParams.id; $scope.showAlert = function(title, text) { $ionicPopup.alert({ title: title, template: text }); }; $scope.refresh = function() { $ionicLoading.show(); $http.get('http://api.openweathermap.org/data/2.5/forecast/daily?id='+$scope.id+'&appid=2de143494c0b295cca9337e1e96b00e0') .success(function(data, status, headers, config) { $scope.data = data; $ionicLoading.hide(); $scope.$broadcast('scroll.refreshComplete'); }) .error(function(data, status, headers, config) { $ionicLoading.hide(); $scope.showAlert(status, data); $scope.$broadcast('scroll.refreshComplete'); }); }; $scope.refresh(); })
Мы добавили условие, чтобы пользователя перекидывало на настроенный город, а также незаметно указали в зависимостях компонент $ionicLoading для показа окна ожидания во время запроса. Надеюсь ничего не пропустил, все готово, вы знаете что делать (ionic serve —lab, если забыли):
Заключение
В принципе на текущий момент работать с помощью IF становится всё удобнее и проще, поэтому я смело могу сказать что это один из лучших фреймворков для прототипирования, и даже для production приложений. Дерзайте! В следующей статье хочу описать создание чата с помощью socket.io и работу с ещё кое-чем интересным ;). Если у вас появились трудности, вопросы или предложения для следующих статей — милости прошу в ЛС!
ссылка на оригинал статьи http://habrahabr.ru/post/256387/
Добавить комментарий