В этом уроке мы рассмотрим создание мобильного приложения, которое отображает RSS-контент веб-сайта. Мы настроим URL-адрес RSS, и приложение загрузит его, проанализирует и отобразит сообщения из RSS.
Для создания мобильного приложения мы будем использовать Ionic Framework v1 вместе с AngularJS. Чтобы завершить этот урок, вы должны иметь некоторый опыт работы с JavaScript и HTML. Также полезно, если вы работали с AngularJS раньше.
Если вы никогда ранее не работали с Ionic Framework, я рекомендую хотя бы взглянуть на руководство « Приступая к работе», так как оно дает вам быстрое представление о том, как все работает.
Давайте начнем!
Настройка ионного проекта
Я предполагаю, что вы установили Node в своей системе и у вас также есть npm (менеджер пакетов Node). Установка Ionic Framework так же проста, как запуск следующего:
1
|
npm install -g cordova ionic
|
Это установит Cordova и Ionic на ваш компьютер.
Cordova является основной технологией Ionic, и в основном она позволяет нам иметь встроенный браузер в нашем мобильном приложении. В этом браузере мы сможем запустить весь наш код HTML и JavaScript. Это называется гибридным мобильным приложением, поскольку приложение не запускает собственный код, а работает внутри браузера.
Помимо Cordova, Ionic добавляет к этому возможность использования AngularJS для написания нашего кода, а также добавляет очень аккуратный UI-фреймворк.
С Ionic на месте, мы можем создать наш проект, используя Ionic CLI, очень полезный инструмент командной строки. Ionic предоставляет три шаблона проекта по умолчанию, которые можно использовать в качестве отправной точки:
- blank: как следует из названия, это пустой проект с минимально необходимыми компонентами.
- вкладки: приложение, использующее вкладки для навигации по экранам.
- sidemenu: приложение, использующее стандартное мобильное боковое меню для навигации.
Для этого урока мы будем использовать шаблон приложения с вкладками. Чтобы начать наш проект, давайте запустим:
1
|
ionic start myWebsiteOnMobile tabs
|
Ionic загрузит и установит все необходимые компоненты и создаст папку проекта с именем myWebsiteOnMobile
. Перейдите в каталог проекта, выполнив:
1
|
cd myWebsiteOnMobile
|
Поскольку наше приложение является гибридным мобильным приложением, мы имеем преимущество в том, что можем запускать приложение в браузере. Для этого Ionic предоставляет аккуратный встроенный веб-сервер, который запускает наше приложение следующим образом:
1
|
ionic serve
|
Это откроет браузер с загруженным нашим приложением и будет выглядеть так:
Чтобы остановить сервер, используйте Control-C на экране командной строки. Чтобы лучше понять, как приложение выглядит на мобильном телефоне, вы можете использовать:
1
|
ionic serve —lab
|
Это откроет приложение в браузере, показывая iOS и предварительный просмотр приложения Android рядом друг с другом.
Вкладки Шаблон приложения Ionic имеет три вкладки: Статус, Чаты и Аккаунт. На следующих шагах мы настроим приложение в соответствии с нашими потребностями.
Как настроить приложение-шаблон Ionic Tabs по умолчанию
Для нашего приложения у нас будет две вкладки:
- Последние сообщения: отображение списка последних сообщений, полученных из RSS-канала.
- Настройки: где пользователь сможет настроить несколько аспектов приложения.
На вкладке «Последние сообщения» пользователь сможет щелкнуть любое из последних сообщений и просмотреть дополнительную информацию о сообщении с возможностью открытия сообщения во внешнем браузере.
Поскольку наша вкладка «Последние сообщения» аналогична вкладке «Чаты», предоставляемой шаблоном, мы будем использовать ее вместе с вкладкой «Аккаунт», которая станет нашей вкладкой «Настройки». Мы можем выполнить все модификации с запущенным веб-сервером Ionic, и Ionic перезагрузит приложение для нас. Это очень аккуратная функция, которая ускорит разработку.
Как упоминалось ранее, Ionic использует AngularJS, и все приложение фактически является модулем AngularJS. Модуль определен в www/js/app.js
, и здесь также www/js/app.js
пути или маршруты приложения. Каждый экран приложения имеет соответствующий маршрут.
Давайте удалим вкладку Статус, так как она нам не понадобится. Для этого сначала нужно изменить экран (или маршрут) нашего приложения по умолчанию, чтобы он указывал на экран чатов, который станет нашим основным экраном. Экран по умолчанию настраивается через $urlRouterProvider.otherwise()
, поэтому давайте изменим его на:
1
|
$urlRouterProvider.otherwise(‘/tab/chats’);
|
Если мы теперь перезагрузим http://localhost:8100
в нашем браузере, мы увидим, что вкладка Чаты будет загружена по умолчанию.
Чтобы удалить вкладку «Состояние», нам нужно отредактировать файл www/templates/tabs.html
, содержащий шаблон для компонента вкладки. Мы удалим элемент:
1
2
3
4
|
<!— Dashboard Tab —>
<ion-tab title=»Status» icon-off=»ion-ios-pulse» icon-on=»ion-ios-pulse-strong» href=»#/tab/dash»>
<ion-nav-view name=»tab-dash»></ion-nav-view>
</ion-tab>
|
При сохранении мы увидим, что приложение теперь имеет только две вкладки: Чаты и Аккаунт.
В файле www/templates/tabs.html
мы заметили, что есть некоторые теги HTML, которые не являются стандартными HTML, такие как ion-tabs
, ion-tab
и ion-nav-view
. На самом деле это директивы AngularJS, определенные Ionic Framework. Директивы представляют собой теги, которые упаковывают функциональность за ними, и они являются очень удобными способами написания более структурированного и более лаконичного кода.
В нашем случае директива ion-tabs
является компонентом tabs, который для каждой вкладки требует директивы ion-tabs
.
Давайте изменим наши вкладки с чата и учетной записи на наши необходимые имена Последние сообщения и настройки. Для этого мы www/templates/tabs.html
несколько вещей в файле www/templates/tabs.html
:
- Атрибут
title
элементовion-tab
определяющий текст на кнопке tab. Мы изменим это на Последние сообщения и Настройки соответственно. - Атрибут
href
элементовion-tab
который указывает на маршрут или URL экрана. Мы изменим их на#/tab/latest-posts
и#/tab/settings
. - Атрибут
name
элементовion-nav-view
дляtab-latest-posts
иtab-settings
. Это идентификаторы для шаблонов представления, используемых для последних сообщений и экранов настроек.
В результате, www/templates/tabs.html
должен выглядеть так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<ion-tabs class=»tabs-icon-top tabs-color-active-positive»>
<!— Latest posts Tab —>
<ion-tab title=»Latest posts» icon-off=»ion-ios-chatboxes-outline» icon-on=»ion-ios-chatboxes» href=»#/tab/latest-posts»>
<ion-nav-view name=»tab-latest-posts»></ion-nav-view>
</ion-tab>
<!— Settings Tab —>
<ion-tab title=»Settings» icon-off=»ion-ios-gear-outline» icon-on=»ion-ios-gear» href=»#/tab/settings»>
<ion-nav-view name=»tab-settings»></ion-nav-view>
</ion-tab>
</ion-tabs>
|
После внесения этих изменений мы получим некоторые ошибки. Это потому, что мы также должны настроить наши маршруты, чтобы использовать новые идентификаторы, которые мы использовали. В www/js/app.js
нам нужно изменить идентификаторы состояния, идентификаторы представления и url
для каждого маршрута в соответствии с тем, что мы установили выше.
Для каждого маршрута (или экрана) определен контроллер. Это базовый шаблон проектирования MVC (Model-View-Controller). Контроллеры определены в файле www/js/controllers.js
. В целях обеспечения согласованности мы изменим имена контроллеров как в www/js/app.js
и в www/js/controller.js
:
-
ChatsCtrl
становитсяChatsCtrl
. -
ChatDetailCtrl
становитсяPostDetailCtrl
. -
AccountCtrl
становитсяSettingsCtrl
.
Кроме того, для каждого маршрута у нас есть определенный шаблон представления, поэтому давайте изменим их тоже. Отредактируйте www/js/app.js
и измените templateUrl
следующим образом:
- Измените
tab-chats.html
наtab-latest-posts.html
. Также переименуйте файлwww/templates/tab-chats.html
вwww/templates/tab-latest-posts.html
. - Измените
chat-detail.html
наpost-detail.html
. Также переименуйте файлwww/templates/chat-detail.html
вwww/templates/post-detail.html
. - Измените
tab-account.html
наtab-settings.html
. Также переименуйте файлwww/templates/tab-account.html
вwww/templates/tab-settings.html
. - Наконец, измените представление, которое загружается по умолчанию, на
latest-posts
с помощью$urlRouterProvider.otherwise('/tab/latest-posts')
.
Если все прошло хорошо, вы должны www/js/app.js
файл www/js/app.js
который выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
…
// Each tab has its own nav history stack:
.state(‘tab.latest-posts’, {
url: ‘/latest-posts’,
views: {
‘tab-latest-posts’: {
templateUrl: ‘templates/tab-latest-posts.html’,
controller: ‘LatestPostsCtrl’
}
}
})
.state(‘tab.post-detail’, {
url: ‘/latest-posts/:postId’,
views: {
‘tab-latest-posts’: {
templateUrl: ‘templates/post-detail.html’,
controller: ‘PostDetailCtrl’
}
}
})
.state(‘tab.settings’, {
url: ‘/settings’,
views: {
‘tab-settings’: {
templateUrl: ‘templates/tab-settings.html’,
controller: ‘SettingsCtrl’
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise(‘/tab/latest-posts’);
…
|
И наш очищенный файл www/js/controllers.js
выглядит так:
1
2
3
4
|
angular.module(‘starter.controllers’, [])
.controller(‘LatestPostsCtrl’, function($scope) {})
.controller(‘PostDetailCtrl’, function($scope, $stateParams) {})
.controller(‘SettingsCtrl’, function($scope) {});
|
Теперь, когда мы реструктурировали приложение в соответствии с нашими потребностями, давайте перейдем к следующей части и добавим некоторые функциональные возможности.
Как получить RSS-канал с помощью Ionic
Чтобы отобразить список последних сообщений, нашему приложению потребуется извлечь RSS-канал с URL-адреса. Рекомендуется, чтобы такая функциональность находилась на уровне обслуживания приложения. Таким образом, мы можем легче использовать его в нашем контроллере, а затем представить его пользователю с помощью представления.
Служба RSS будет использовать YQL REST API от Yahoo для получения RSS нашего сайта. Для вызова REST API мы будем использовать поставщика $http
, предлагаемого AngularJS.
Ионные сервисы обычно определяются в файле www/js/services.js
, так что мы также разместим наши. Код будет выглядеть так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
angular.module(‘starter.services’, [])
.service(‘RSS’,function($http){
var self = this;
this.download = function(next){
var feedUrl = «https://tutorials.tutsplus.com/posts.atom»;
var yql_query = «select * from xml where url = ‘»+feedUrl+»‘»;
var url = ‘https://query.yahooapis.com/v1/public/yql?q=’+encodeURIComponent(yql_query)+’&format=json&callback=JSON_CALLBACK’;
$http
.jsonp(url)
.success(function(response){
if (response.query.results[«feed»]){
next(self.parseAtom(response.query.results));
} else if (response.query.results[«rss»]){
next(self.parseRSS(response.query.results));
} else {
throw «Unknown RSS format»;
}
})
.error(function(data, status){
});
}
this.parseAtom = function(atomFeed){
var posts = [];
angular.forEach(atomFeed.feed.entry,function(item,idx){
var post = {
id:idx,
title:item.title,
description:item.content.content,
link:item.link.href
}
posts.push(post);
})
return posts;
}
this.parseRSS = function(rssFeed){
var posts = [];
angular.forEach(rssFeed.rss.channel.item,function(item,idx){
var post = {
id:idx,
title:item.title,
description:item.description,
link:item.link
}
posts.push(post);
})
return posts;
}
})
|
Мы объявляем службу, используя метод service()
предоставляемый AngularJS. Затем мы вводим модуль $http
Angular, чтобы мы могли вызывать его в нашем сервисе.
Переменная self
является ссылкой на службу RSS, поэтому мы можем вызывать ее из методов службы. Основным методом службы является метод download()
, который загружает информацию канала и обрабатывает ее. Существует два основных формата, используемых для веб-каналов: RSS и ATOM. Для нашего приложения мы использовали ленту учебников из Tuts + https://tutorials.tutsplus.com/posts.atom, которая находится в формате ATOM, но для полноты картины мы также приняли во внимание формат RSS.
Метод download()
вызывает API-интерфейс YQL и анализирует результаты, используя parseAtom()
или parseRSS()
зависимости от типа канала. Идея здесь состоит в том, чтобы иметь тот же формат вывода, который будет передан через обратный вызов next()
. С сервисом RSS на месте, мы можем перейти к контроллеру.
Подключение службы RSS к контроллеру последних сообщений
В нашем файле www/js/controllers.js
нам нужно загрузить данные RSS и передать их нашему представлению. Для этого нам нужно только изменить наш контроллер LatestPostsCtrl
следующим образом:
1
2
3
4
5
|
.controller(‘LatestPostsCtrl’, function($scope, RSS) {
RSS.download(function(posts){
$scope.posts = posts;
});
})
|
Используя механизм внедрения зависимостей Angular, нам нужно только указать переменные $scope
и RSS
качестве параметров метода, и он будет знать, как загрузить эти модули. Модуль $scope
позволяет нам устанавливать переменные в модели, связанной с представлением. Любые значения, установленные в области, могут быть затем извлечены и отображены в представлении, связанном с контроллером.
Когда представление для последних сообщений загружается, он вызывает контроллер LatestPostsCtrl
, который, в свою очередь, использует службу RSS для загрузки информации о фиде. Результаты анализируются и передаются обратно в виде массива с помощью переменной posts
, которую мы храним в текущей области видимости.
После всего этого мы можем перейти к представлению, отображающему список сообщений, полученных из канала.
Отслеживание последних сообщений Просмотр данных канала
Теперь нам нужно изменить наш взгляд на последние сообщения. Если вы помните, это настраивается в файле www/js/app.js
через атрибут templateUrl
и указывает на файл www/templates/tab-latest-posts.html
.
Что мы хотим сделать, это отобразить список каналов. Поскольку информация о фиде может содержать HTML, и это будет только загромождать список последних сообщений, нам нужно что-то извлечь из тега содержимого без тегов HTML. Самый простой способ сделать это — определить фильтр AngularJS, который удаляет теги HTML из текста. Давайте сделаем это в www/js/services.js
, добавив:
1
2
3
4
5
6
|
.filter(‘htmlToPlaintext’, function() {
return function(text) {
return text ?
};
}
)
|
Не вернемся к нашему представлению в файле www/templates/tab-latest-posts.html
, давайте www/templates/tab-latest-posts.html
его так:
01
02
03
04
05
06
07
08
09
10
11
12
|
<ion-view view-title=»Latest posts»>
<ion-content>
<ion-list>
<ion-item class=»item-icon-left item-icon-right» ng-repeat=»post in posts» type=»item-text-wrap» href=»#/tab/latest-posts/{{post.id}}»>
<span class=»icon ion-social-rss-outline»>
<h2>{{post.title}}</h2>
<p>{{post.description |
<i class=»icon ion-chevron-right icon-accessory»></i>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
|
Мы используем компонент пользовательского интерфейса Ionic list вместе с директивой Angular ng-repeat
, которая будет перебирать posts
установленные в области действия нашего контроллера. Для каждой записи поста у нас будет элемент списка с его заголовком и описанием, очищенным от тегов HTML с помощью фильтра htmlToPlaintext
. Также обратите внимание, что нажатие на сообщение должно привести нас к деталям сообщения из-за атрибута href
установленного в #/tab/latest-posts/{{post.id}}
. Это пока не работает, но мы позаботимся об этом в следующем разделе.
Если мы теперь запустим приложение, используя ionic serve --lab
, мы должны получить что-то вроде этого:
Отображение деталей сообщения
При нажатии на сообщение в списке мы переходим на экран сведений о сообщении в приложении. Поскольку у каждого экрана приложения есть свой контроллер и, следовательно, своя область действия, мы не можем получить доступ к списку сообщений для отображения определенного сообщения. Мы можем снова вызвать службу RSS, но это было бы неэффективно.
Чтобы решить эту проблему, мы можем использовать директиву $rootScope
предлагаемую Angular. Это относится к области действия, которая охватывает все контроллеры в приложении. Давайте $rootScope
наш LatestPostCtrl
чтобы установить сообщения в $rootScope
а затем $rootScope
поиск определенного сообщения, которое пользователь щелкнул в PostDetailCtrl
. Результирующий код в www/js/controllers.js
будет выглядеть так:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
.controller(‘LatestPostsCtrl’, function($scope, $rootScope, RSS) {
RSS.download(function(posts){
$rootScope.posts = posts;
});
})
.controller(‘PostDetailCtrl’, function($scope,$rootScope, $stateParams) {
angular.forEach($rootScope.posts,function(post){
if (post.id == $stateParams.postId){
$scope.post = post;
}
})
})
|
Мы просто ввели $rootScope
в оба контроллера и использовали его для передачи posts
между двумя контроллерами. Обратите внимание, что нам не нужно вносить какие-либо изменения в наше последнее представление сообщений, так как $rootScope
и $scope
оба доступны одинаково из представления.
Внутри контроллера PostDetailCtrl
мы просто ищем пост с идентификатором, переданным в ссылке, по которой щелкнул пользователь. Мы делаем это, сравнивая каждый идентификатор сообщения со значением в URL, передаваемом через переменную $stateParams.postId
. Если мы найдем совпадение, мы установим сообщение в области видимости, чтобы мы могли использовать его в нашем представлении.
Давайте теперь настроим наш подробный вид поста www/templates/post-detail.html
следующим образом:
01
02
03
04
05
06
07
08
09
10
11
|
<ion-view view-title=»{{post.title}}»>
<ion-nav-buttons side=»right»>
<a ng-href=»{{post.link}}» class=»button» target=»_system»>
Open
</a>
</ion-nav-buttons>
<ion-content class=»padding»>
<h1>{{post.title}}</h1>
<span ng-bind-html=»post.description»>
</ion-content>
</ion-view>
|
Это то, что мы сделали в представлении:
- Мы поместили заголовок поста в заголовок экрана.
- Мы поместили кнопку «Открыть» в заголовке справа. Эта кнопка откроет ссылку на публикацию во внешнем браузере из-за атрибута
target="_system"
. Мы должны сделать это, потому что приложение уже запущено в браузере из-за Cordova. Если бы мы не установили этот атрибут, публикация открылась бы в том же браузере, что и приложение, и тогда у нас не было бы способа вернуться в приложение. - Мы выводим описание поста в виде HTML с помощью директивы Angular
ng-bind-html
.
Во время работы приложения я заметил, что если описание поста содержит изображения, некоторые из них выпадают из экрана. Это может быть в случае с другими элементами HTML, такими как видео. Мы можем легко это исправить, добавив следующее правило CSS в www/css/style.css
.
1
2
3
|
ion-content *{
max-width: 100%;
}
|
Если мы сейчас посмотрим на приложение и нажмем на одну из публикаций, мы должны увидеть что-то вроде этого:
И наше приложение почти завершено. В следующем разделе мы рассмотрим реализацию экрана настроек.
Добавление настроек для нашего ионного приложения
Для нашего экрана настроек мы реализуем способ указания количества сообщений, отображаемых на главном экране приложения. Мы сохраним этот параметр в памяти localStorage
, которая не стирается при закрытии приложения. Давайте отредактируем файл контроллеров www/js/controllers.js
и изменим контроллер SettingsCtrl
следующим образом:
01
02
03
04
05
06
07
08
09
10
|
.controller(‘SettingsCtrl’, function($scope,$rootScope) {
$scope.settings = {
maxPosts: window.localStorage.getItem(«myWebsiteOnMobile.maxPosts»)
};
$scope.$watch(‘settings.maxPosts’,function(){
window.localStorage.setItem(«myWebsiteOnMobile.maxPosts»,$scope.settings.maxPosts);
$rootScope.maxPosts = window.localStorage.getItem(«myWebsiteOnMobile.maxPosts»);
});
});
|
Также нам нужно изменить экран настроек в www/templates/tab-settings.html
следующим образом:
1
2
3
4
5
6
7
8
|
<ion-view view-title=»Settings»>
<ion-content>
<div class=»item item-divider item-balanced»>Maximum posts</div>
<ion-radio ng-model=»settings.maxPosts» ng-value=»null»>Unlimited</ion-radio>
<ion-radio ng-model=»settings.maxPosts» ng-value=»5″>5</ion-radio>
<ion-radio ng-model=»settings.maxPosts» ng-value=»10″>10</ion-radio>
</ion-content>
</ion-view>
|
Контроллер получает настройку myWebsiteOnMobile.maxPosts
из localStorage
. Если он не существует, он будет null
, и мы будем считать, что нет ограничения на максимальное количество сообщений.
Мы вызываем метод $scope.$watch()
для отслеживания изменений переменной settings.maxPosts
, которая привязана к радиоуправлению на экране настроек.
С учетом всего этого каждый раз, когда мы меняем максимальное количество сообщений на экране настроек, настройки будут сохраняться в localStorage
и извлекаться из него при перезапуске приложения.
Теперь давайте воспользуемся этим параметром. Это так же просто, как добавить это в LatestPostsCtrl
из www/js/controllers.js
:
1
|
$rootScope.maxPosts = window.localStorage.getItem(«myWebsiteOnMobile.maxPosts»);
|
И добавление директивы на экране последних сообщений www/templates/tab-latest-posts.html
:
1
|
<ion-item class=»item-icon-left item-icon-right» ng-repeat=»post in posts|limitTo:maxPosts» type=»item-text-wrap» href=»#/tab/latest-posts/{{post.id}}»>
|
Обратите внимание на limitTo:maxPosts
Угловой фильтр. Это ограничит количество отображаемых сообщений числом, взятым из localStorage
. По умолчанию это будет null
, который будет отображать все каналы, полученные службой RSS.
Поздравляем! Теперь у нас есть полностью работающее приложение, отображающее RSS-канал.
Вывод
В этом руководстве мы увидели, как создать гибридное мобильное приложение с использованием Ionic Framework и AngularJS. Осталось сделать только одно: запустить приложение на мобильном устройстве или в мобильном эмуляторе. Это очень просто с Ionic. Чтобы запустить приложение на эмуляторе Android, просто запустите:
1
2
|
ionic platform add android
ionic run
|
Если вы хотите загрузить готовый шаблон приложения Ionic для преобразования любого веб-сайта в мобильное приложение, попробуйте перейти на веб-сайт в шаблон приложения Mobile Ionic из CodeCanyon.