![]() |
圖1. 側欄選單 |
建立「側欄選單」
假設要在home.html建立「側欄選單」的主要頁面則必須完成下列基本內容:
其中包括:
- <ion-side-menus>
- <!-- 主要內容 -->
- <ion-side-menu-content>
- <!-- 主要內容頁面區塊 -->
- <ion-nav-view animation="slide-left-right" name="menuContent">
- </ion-nav-view>
- </ion-side-menu-content>
- <!-- 左側選單 -->
- <ion-side-menu side="left">
- </ion-side-menu>
- <!-- 右側選單 -->
- <ion-side-menu side="right">
- </ion-side-menu>
- </ion-side-menus>
- 使用<ion-side-menus>主要指令,包含所有側欄選單的內容,包括:主要內容區、左側選單區、與右側選單區。左右側選單如無定義內容,則不會顯示。
- 使用<ion-side-menu-content>定義主要內容區:裡面除了使用<ion-nav-view>建立主要區塊,同時設定name屬性(如第5行menuContent即為view的名稱)外,也可加入<ion-nav-bar>等其他用來建立多頁面apps的指令。
- 使用<ion-side-menu>建立左右側選單。至於選單出現在哪一側,則由side屬性決定之(如第10行與第14行)。
完成這兩部分之後,便可透過左右滑動手勢,帶出或隱藏兩側選單。
- .config(function ($stateProvider, $urlRouterProvider) {
- $urlRouterProvider.otherwise("/app/members");
- $stateProvider
- .state('app', {
- url: '/app',
- abstract: true,
- templateUrl: 'templates/home.html'
- })
- .state('app.members', {
- url: '/members',
- views: {
- 'menuContent': {
- templateUrl: 'templates/members.html',
- controller: 'memberCtrl'
- }
- }
- })
- ...[略]...
範例:Spotify歌曲試聽
此範例使用Spotify Web API取得歌手資料,如專輯、熱門歌曲等,並提供30秒的歌曲試聽。其中使用了三個API-搜尋、取得專輯列表、取得熱門歌曲,後兩者都必須先有歌手id才能進行查詢:https://api.spotify.com/v1/search https://api.spotify.com/v1/artists/{id}/albums https://api.spotify.com/v1/artists/{id}/top-tracks其他API說明,可參考Spotify API Console。
範例程式由圖2畫面為程式進入點,該畫面由sidemenu.html為parent state,並帶入artists.html,顯示歌手基本資料。
![]() |
圖2. Spotify歌曲試聽App |
- 第16行:定義主要內容頁面區塊的name屬性值為menuContent。
- 第21-35行:定義左側選單共兩個項目。
js/app.js設定config()如下:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('starter', ['ionic','starter.controllers', 'starter.services']) | |
.run(function ($ionicPlatform) { | |
$ionicPlatform.ready(function () { | |
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard | |
// for form inputs) | |
if (window.cordova && window.cordova.plugins.Keyboard) { | |
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); | |
} | |
if (window.StatusBar) { | |
StatusBar.styleDefault(); | |
} | |
}); | |
}) | |
.config(function ($stateProvider, $urlRouterProvider) { | |
$urlRouterProvider.otherwise("/app/artists"); | |
$stateProvider | |
.state('app', { | |
url: '/app', | |
abstract: true, | |
templateUrl: 'templates/sidemenu.html' | |
}) | |
.state('app.artists', { | |
url: '/artists', | |
views: { | |
'menuContent': { | |
templateUrl: 'templates/artists.html', | |
controller: 'artistsCtrl' | |
} | |
} | |
}) | |
.state('app.artist', { | |
url: '/artists/:id', | |
views: { | |
menuContent: { | |
templateUrl: 'templates/artist-details.html', | |
controller: 'detailCtrl' | |
} | |
} | |
}) | |
.state('app.track', { | |
url: '/tracks/:id', | |
views: { | |
menuContent: { | |
templateUrl: 'templates/artist-tracks.html', | |
controller: 'trackCtrl' | |
} | |
} | |
}) | |
.state('app.tracks', { | |
url: '/tracks', | |
views: { | |
menuContent: { | |
templateUrl: 'templates/tracks.html', | |
controller: 'alltrackCtrl' | |
} | |
} | |
}); | |
}) |
- 第17-21行:定義parent state,頁面檔案即為上述 templates/sidemenu.html。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<ion-view title="歌手"> | |
<ion-content> | |
<ion-list> | |
<ion-item class="item item-avatar" ng-repeat="artist in artists"> | |
<img ng-src="{{artist.image}}"> | |
<h2>{{artist.name}}</h2> | |
<div class="item item-body"> | |
<button class="button-clear ion-information" ng-click="details($index)"> | |
介紹</button> | |
<button class="button-clear ion-music-note" ng-click="tracks($index)"> | |
熱門歌曲</button> | |
</div> | |
<div class="item tabs tabs-icon-left"> | |
<div class="tab-item"><i class="icon ion-thumbsup"></i>{{artist.popularity}}</div> | |
<div class="tab-item"><i class="ion-social-twitter-outline"></i>{{artist.followers}}</div> | |
</div> | |
</ion-item> | |
</ion-list> | |
</ion-content> | |
</ion-view> |
搭配的js/controllers.js如下:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('starter.controllers', []) | |
.controller('alltrackCtrl', function ($scope, $ionicModal, $filter) { | |
$scope.artists = artists; | |
$ionicModal.fromTemplateUrl('templates/track-details.html', { | |
scope: $scope, | |
animation: 'slide-in-up' | |
}).then(function (modal) { | |
$scope.modal = modal; | |
}); | |
$scope.openModal = function (singerID, index) { | |
var result = $scope.artists.filter(function (item) { | |
return (item.id === singerID) | |
}) | |
$scope.artist = result[0]; | |
$scope.track = $scope.artist.tracks[index]; | |
$scope.play($scope.track.preview_url); | |
$scope.modal.show(); | |
}; | |
$scope.closeModal = function () { | |
$scope.pause(); | |
$scope.modal.hide(); | |
}; | |
$scope.play = function (url) { | |
if (media) | |
media.pause(); | |
media = new Audio(url); | |
media.play(); | |
}; | |
$scope.pause = function () { | |
if (media) | |
media.pause(); | |
}; | |
$scope.$on('$destroy', function () { | |
if (media) | |
media.pause(); | |
}); | |
}) | |
.controller('artistsCtrl', function ($scope, $location, singers, MusicPlayer) { | |
if (artists.length === 0) { // 尚未擷取基本資料 | |
//artists = []; | |
angular.forEach(singers, function (value) { | |
var singer = []; | |
singer.name = value; | |
MusicPlayer.basicData(value).then(function (result) { | |
singer.id = result.data.artists.items[0].id; | |
singer.popularity = result.data.artists.items[0].popularity; | |
singer.followers = result.data.artists.items[0].followers['total']; | |
singer.image = result.data.artists.items[0].images[2]['url']; | |
var albums = []; | |
MusicPlayer.getAlbums(singer.id).then(function (result) { | |
angular.forEach(result.data.items, function (value) { | |
var album = []; | |
album.name = value.name; | |
album.id = value.id; | |
album.image = value.images[1]['url']; | |
albums.push(album); | |
}) | |
}, function (error) { | |
console.warn(error); | |
}); | |
singer.albums = albums; | |
var tracks = []; | |
MusicPlayer.getTracks(singer.id).then(function (result) { | |
angular.forEach(result.data.tracks, function (value) { | |
var track = []; | |
track.name = value.name; | |
track.image = value.album.images[1]['url']; | |
track.popularity = value.popularity; | |
track.preview_url = value.preview_url; | |
tracks.push(track); | |
}) | |
}, function (error) { | |
console.warn(error); | |
}) | |
singer.tracks = tracks; | |
artists.push(singer); | |
}, function (error) { | |
console.warn(error); | |
}) | |
}) | |
} | |
$scope.artists = artists; | |
$scope.details = function (index) { | |
$location.path('/app/artists/' + index); | |
}; | |
$scope.tracks = function (index) { | |
$location.path('/app/tracks/' + index); | |
}; | |
}) | |
.controller('detailCtrl', function ($scope, $stateParams) { | |
$scope.artist = artists[$stateParams.id]; | |
}) | |
.controller('trackCtrl', function ($scope, $stateParams, $ionicModal) { | |
$scope.artist = artists[$stateParams.id]; | |
$ionicModal.fromTemplateUrl('templates/track-details.html', { | |
scope: $scope, | |
animation: 'slide-in-up' | |
}).then(function (modal) { | |
$scope.modal = modal; | |
}); | |
$scope.openModal = function (index) { | |
$scope.track = $scope.artist.tracks[index]; | |
$scope.play($scope.track.preview_url); | |
$scope.modal.show(); | |
}; | |
$scope.closeModal = function () { | |
$scope.pause(); | |
$scope.modal.hide(); | |
}; | |
$scope.play = function (url) { | |
if (media) | |
media.pause(); | |
media = new Audio(url); | |
media.play(); | |
}; | |
$scope.pause = function () { | |
if (media) | |
media.pause(); | |
}; | |
$scope.$on('$destroy', function () { | |
if (media) | |
media.pause(); | |
$scope.modal.remove(); | |
}); | |
}); | |
var media; | |
var artists = []; |
- 第38-89行:為artists.html的controller: artistsCtrl。主要透過自訂service:MusicPlayer以Spotify API取得基本資料等內容。第38行singers為自訂常數 (定義於js/services.js),內為要搜尋的歌手姓名。
- 第44-80行:以Spotify API擷取資料的主要流程,分為第44行的搜尋歌手基本資料(自訂服務basicData()),並利用第45行取得的歌手id,在第50行(自訂服務getAlbums())進一步查詢專輯、以及第63行(自訂服務getTracks())查詢熱門歌曲。
提供Spotify Web API服務js/services.js如下:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('starter.services', []) | |
.factory('SpotifyWS', function ($http, $q) { | |
var self = this; | |
self.search = function (method, api) { | |
var q = $q.defer(); | |
switch (method) { | |
case 'GET': | |
$http.get(api).then(function (result) { | |
q.resolve(result); | |
}, function (error) { | |
console.warn(error); | |
q.reject(error); | |
}); | |
break; | |
} | |
return q.promise; | |
} | |
return this; | |
}) | |
.factory('MusicPlayer', function (SEARCH_API, SpotifyWS) { | |
var self = this; | |
self.basicData = function (singer) { | |
return SpotifyWS.search('GET', SEARCH_API + '?q=' + singer + '&type=artist'); | |
} | |
self.getAlbums = function (singerID) { | |
return SpotifyWS.search('GET', 'https://api.spotify.com/v1/artists/' + singerID + '/albums'); | |
} | |
self.getTracks = function (singerID) { | |
return SpotifyWS.search('GET', 'https://api.spotify.com/v1/artists/' + singerID + '/top-tracks?country=TW'); | |
} | |
return this; | |
}) | |
.value('singers', ['張惠妹', '江蕙', '周杰倫', '林俊傑']) | |
.value('SEARCH_API', "https://api.spotify.com/v1/search") |
- 第2-19行:以$http.get(api)使用Spotify Web API取得資料
- 第20-33行:自訂三個服務-basicData(singer)透過搜尋歌手姓名取得基本資料(需注意搜尋結果有時不止一筆)、getAlbums(id)取得指定id的專輯列表、getTracks(id)取得指定id的歌手熱門歌曲列表及對應的試聽網址。
- 第35行:定義4位歌手姓名的常數陣列。
沒有留言:
張貼留言