前置作業(1/4):在parse.com建立App
透過API使用Parse雲端資料庫必須先在Parse.com註冊、建立App、並取得App Keys才能順利使用。Parse的帳號啟用之後,點選"Create a new App"建立App,切換到Dashboard,便可看到已經建立的所有Apps,如下圖:![]() |
圖1.Parse.com提供雲端資料庫服務 |
此處以圖1的"todo" App為例,點選todo App,再切換到Core,從該介面便可為App建立雲端資料庫。
前置作業(2/4):建立 Parse雲端資料庫
Parse雲端資料庫是NoSQL,而非關聯式資料庫,其資料庫是由classes組成,每個class各有自己的屬性(下圖+Col可增加屬性),介面如下圖所示:![]() |
圖2. Parse雲端資料庫介面 |
圖2左上角顯示todo App建立了一個Todo class,內有三筆資料。Todo class除了預設的(objectId, createdAt, updatedAt, ACL) 四個屬性之外,另外新增了title, message兩個String類別的屬性,因此共有6個屬性。之後,Ionic專案建立的app將透過Parse REST API存取此資料集。
所有功能都以 "https://api.parse.com"開頭,再串接上圖的URL。不過,不同動作使用的HTTP方法不盡相同,從POST, GET, 到DELETE都有(如HTTP Verb一欄),而且在http header部分還要另加一些項目才行,以Creating Objects為例,官網文件以curl格式說明此動作需要在header加上三個項目X-Parse-Application-Id,X-Parse-REST-API-Key,以及Content-Type:
前置作業(3/4):瞭解Parse REST API格式
下圖是存取物件的API:![]() |
圖3. Parse REST API (https://parse.com/docs/rest) |
curl -X POST \ -H "X-Parse-Application-Id: 你的Application Id" \ -H "X-Parse-REST-API-Key: 你的REST API Key" \ -H "Content-Type: application/json" \ -d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \ https://api.parse.com/1/classes/GameScore因此,將上述curl改為AngularJS的寫法,則必須以$http呼叫REST API,同時設定額外的header項目才行:
其他API的curl範例請參考https://parse.com/docs/rest。
- var data={"score":1337,"playerName":"Sean Plott","cheatMode":false};
- ... [略] ...
- $scope.getAll = function ($http) {
- $http.post('https://api.parse.com/1/classes/GameScore', data, {
- headers: {'X-Parse-Application-Id': "你的Application ID",
- 'X-Parse-REST-API-Key': "你的REST-API-Key",
- 'Content-Type': 'application/json'
- }
- });
- }
前置作業(4/4):將Application Keys加入Ionic專案中
從Dashboard todo專案處選取Keys,會進入Application Keys畫面。以REST API連線而言,需要用到Application ID與REST API Key。此兩個字串在每一次呼叫$http服務時都必須使用,如前置作業(3/4)的程式碼片段第5-6行所示。如圖4將此兩個字串複製起來,加到Ionic專案中。
由於此兩個Keys用在許多地方,故可加入js/services,js,並以.value()定義為常數,程式碼如下:
js/app.js檔因不需開啟資料庫的緣故,變得只需要設定states即可:
Parse雲端資料庫的部份實作於js/services.js,同樣將共通的部份寫成DBA服務,Todo類別的存取則另外以.factory()建立todoParse服務:
![]() |
圖4. Application Keys |
angular.module('starter.services', []) .factory(...[略]... ...[略]... .value('PARSE_KEYS', { APP_ID: '7h9gDEasc63I84aBqO6iVn.....', REST_API_KEY: 'luGnIWHUb9FS53uvA......' })之後變可以PARSE_KEYS.APP_ID與PARSE_KEYS.REST_API_KEY的方式,使用這個key值。
修改記事App
Hybrid Apps開發系列之5.3:使用Cordova外掛存取SQLite資料庫一文曾以SQLite做為記事App的資料庫,現在可改以前置作業(2/4)建立的雲端資料庫儲存資料。js/app.js檔因不需開啟資料庫的緣故,變得只需要設定states即可:
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('/home'); | |
$stateProvider | |
.state('home', { | |
url: '/home', | |
templateUrl: 'templates/home.html', | |
controller: 'homeCtrl' | |
}) | |
.state('edit', { | |
url: '/home/:id', | |
templateUrl: 'templates/edit.html', | |
controller: 'editCtrl' | |
}) | |
}) |
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('DBA', function ($http, $q) { | |
var self = this; | |
self.query = function (method, api, data, header) { | |
var q = $q.defer(); | |
switch (method) { | |
case 'GET': | |
$http.get(api, header).then(function (result) { | |
q.resolve(result); | |
}, function (error) { | |
console.warn(error); | |
q.reject(error); | |
}) | |
break; | |
case 'POST': | |
$http.post(api, data, header).then(function (result) { | |
q.resolve(result); | |
}, function (error) { | |
console.warn(error); | |
q.reject(error); | |
}) | |
break; | |
case 'PUT': | |
$http.put(api, data, header).then(function (result) { | |
q.resolve(result); | |
}, function (error) { | |
console.warn(error); | |
q.reject(error); | |
}) | |
break; | |
case 'DELETE': | |
$http.delete(api, header).then(function (result) { | |
q.resolve(result); | |
}, function (error) { | |
console.warn(error); | |
q.reject(error); | |
}) | |
break; | |
} | |
return q.promise; | |
} | |
return self | |
}) | |
.factory('todoParse', function (DBA, PARSE_KEYS, PARSE_API) { | |
var self = this; | |
var header = {headers: { | |
'X-Parse-Application-Id': PARSE_KEYS.APP_ID, | |
'X-Parse-REST-API-Key': PARSE_KEYS.REST_API_KEY | |
} | |
} | |
var headerJson = { | |
headers: { | |
'X-Parse-Application-Id': PARSE_KEYS.APP_ID, | |
'X-Parse-REST-API-Key': PARSE_KEYS.REST_API_KEY, | |
'Content-Type': 'application/json' | |
} | |
} | |
self.getAll = function () { | |
return DBA.query('GET', PARSE_API, '', header); | |
} | |
self.get = function (objectId) { | |
return DBA.query('GET', PARSE_API + '/' + objectId, '', header); | |
}; | |
self.create = function (object) { | |
return DBA.query('POST', PARSE_API, object, headerJson); | |
} | |
self.update = function (objectId, object) { | |
return DBA.query('PUT', PARSE_API + '/' + objectId, object, headerJson); | |
} | |
self.delete = function (objectId) { | |
return DBA.query('DELETE', PARSE_API + '/' + objectId, '', headerJson); | |
} | |
return self; | |
}) | |
.value('PARSE_KEYS', { | |
APP_ID: '7h9gDEasc6...[略]...', | |
REST_API_KEY: 'luGnIWHUb9...[略]...' | |
}) | |
.value('PARSE_API', "https://api.parse.com/1/classes/Todo"); |
- 第2-43行:DBA服務。如圖3不同的物件存取功能,會用到不同的http傳送方法,因此以switch方式決定使用的方法,以及所需參數。
- 第7-14行:HTTP GET,取出物件或進行查詢時使用。
- 第15-22行:HTTP POST,建立物件時使用,因此參數多了data—亦即要寫入雲端的資料內容。
- 第23-30行:HTTP PUT,更新物件內容時使用。
- 第31-38行:HTTP DELETE,刪除物件時使用。
- 關於第5行$q.defer()與回傳值q.promise等與非同步執行有關的說明,請參見Hybrid Apps開發系列之5.3:使用Cordova外掛存取SQLite資料庫一文。
- 第44-75行:Todo class資料存取服務。資料存取包含:建立、讀取、更新、刪除等CRUD資料存取基本功能,分別對應到getAll, get(objectId), create(object), update(object), delete(objectId)等函式。
- 第75-79行:Parse API Key常數。請記得改為自己的App id。
- 第80行:Parse API基本網址常數,隨著不同存取功能,可能需要在此常數之後加上objectId。
- home.html各筆資料的超連結設定改用雲端資料庫的objectId欄位值。
- edit.html, addNote.html要做表單驗證,確認各個欄位確實有值,因此加入驗證程式碼。
templates/home.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-nav-buttons side="right"> | |
<button class="button" ng-click="addNote()">新增</button> | |
</ion-nav-buttons> | |
<ion-content> | |
<ion-list class="list list-inset" ng-repeat="item in notes" > | |
<ion-item class="item item-button-right" ng-href="#/home/{{item.objectId}}"> | |
<p>{{item.title}}</p> | |
<span>{{item.message}}</span> | |
<button class="button button-clear"> | |
<i class="icon ion-compose"></i> | |
</button> | |
</ion-item> | |
</ion-list> | |
</ion-content> | |
</ion-view> |
templates/edit.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-nav-buttons side="left"> | |
<button class="button" ng-click="go('#home')">取消返回</button> | |
</ion-nav-buttons> | |
<ion-nav-buttons side="right"> | |
<button class="button" ng-click="update(note)" | |
ng-disabled="editForm.title.$invalid || editForm.message.$invalid"> | |
修改 | |
</button> | |
<button class="button" ng-click="del(note)">刪除</button> | |
</ion-nav-buttons> | |
<ion-content> | |
<form name="editForm" novalidate> | |
<label class="item item-input"> | |
<span class="input-label">標題</span> | |
<input type="text" ng-model="note.title" name="title" required> | |
</label> | |
<label class="item"> | |
<span class="item-note" ng-show="editForm.title.$error.required">標題不得為空</span> | |
</label> | |
<label class="item item-input"> | |
<textarea placeholder="輸入記事內容" ng-model="note.message" name="message" required></textarea> | |
</label> | |
<label class="item"> | |
<span class="item-note" ng-show="editForm.message.$error.required">內容不得為空</span> | |
</label> | |
</form> | |
</ion-content> | |
</ion-view> |
templates/addNote.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-modal-view> | |
<ion-header-bar class="bar-energized"> | |
<h1 class="title">新增記事</h1> | |
<button class="button" ng-click="closeModal()">返回</button> | |
</ion-header-bar> | |
<ion-content> | |
<form name="addForm" novaliate> | |
<label class="item item-input"> | |
<span class="input-label">標題</span> | |
<input type="text" placeholder="輸入標題" ng-model="note.title" name="title" required> | |
</label> | |
<label class="item"> | |
<span class="item-note" ng-show="addForm.title.$error.required">標題不得為空</span> | |
</label> | |
<label class="item item-input"> | |
<textarea placeholder="輸入記事內容" ng-model="note.message" | |
name="message" required></textarea> | |
</label> | |
<label class="item"> | |
<span class="item-note" ng-show="addForm.message.$error.required">內容不得為空</span> | |
</label> | |
<button class="button button-block" ng-click="create(note)" | |
ng-disabled="addForm.title.$invalid | |
|| addForm.message.$invalid">新增</button> | |
</form> | |
</ion-content> | |
</ion-modal-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('homeCtrl', function ($scope, todoParse, $ionicModal) { | |
$scope.updateNotes = function () { | |
todoParse.getAll().then(function (result) { | |
$scope.notes = result.data.results; | |
}); | |
} | |
$scope.updateNotes(); // 讀取資料 | |
$scope.addNote = function () { // 開啟modal視窗,填寫資料存入資料庫 | |
$scope.note = {}; // 新增記事使用 | |
$scope.openModal(); //開啟視窗 | |
}; | |
$scope.create = function (note) { // 新增記事 | |
todoParse.create(note).then(function (success) { | |
$scope.closeModal(); | |
$scope.updateNotes(); // 更新資料 | |
}); | |
} | |
$ionicModal.fromTemplateUrl('templates/addNote.html', {// modal視窗定義 | |
scope: $scope, | |
animation: 'silde-in-up' | |
}).then(function (modal) { | |
$scope.modal = modal; | |
}) | |
$scope.openModal = function () { | |
$scope.modal.show(); | |
} | |
$scope.closeModal = function () { | |
$scope.modal.hide(); | |
} | |
$scope.$on('$destroy', function () { | |
$scope.modal.remove(); | |
}) | |
}) | |
.controller('editCtrl', function ($scope, todoParse, $stateParams, $location, $window) { | |
todoParse.get($stateParams.id).then(function (result) { | |
$scope.note = result.data; | |
}, function (error) { | |
console.warn(error); | |
}) | |
$scope.update = function (note) { | |
todoParse.update($stateParams.id, note).then(function (success) { | |
$window.location.reload(true); | |
$window.location.href = '#home'; | |
}); | |
} | |
$scope.del = function () { | |
var promise = todoParse.delete($stateParams.id).then(function (success) { | |
$window.location.reload(true); | |
$window.location.href = '#home'; | |
}); | |
} | |
$scope.go = function (path) { | |
$location.path(path); | |
} | |
}) | |
沒有留言:
張貼留言