67
Parse Basic Building Web Apps WITHOUT Programming Server. http://goo.gl/8IqkAa 2014 Spring Web Programming, NCCU Author: pa4373 (Licensed by CC-By 4.0)

Introduction to Parse JavaScript SDK

  • Upload
    -

  • View
    420

  • Download
    3

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Introduction to Parse JavaScript SDK

Parse BasicBuilding Web Apps WITHOUT Programming Server.

http://goo.gl/8IqkAa

2014 Spring Web Programming, NCCUAuthor: pa4373 (Licensed by CC-By 4.0)

Page 2: Introduction to Parse JavaScript SDK

So far,你的網頁都是一成不變的

http://www.pmichaud.com/toast/ (從1994年後都沒變過 )

Page 3: Introduction to Parse JavaScript SDK

但實際上,很多網站是隨時在改變的

Page 4: Introduction to Parse JavaScript SDK

為什麼網站可以不斷的、及時變化?

因為有後端啊。

*Web伺服器:接收http請求、產生或委派http回應並傳回client端

*應用程式伺服器:根據使用者的請求產生相對的回應

資料庫:像一張巨大的Excel表,存各地來的資料

Page 5: Introduction to Parse JavaScript SDK
Page 6: Introduction to Parse JavaScript SDK

Backend Technology Stack

Page 7: Introduction to Parse JavaScript SDK

What if?

如果有一個工具可以讓我不用寫後端程式碼,但還是可以使用後端的功能,該有多好啊......

Page 8: Introduction to Parse JavaScript SDK

Parse來拯救咧!

Page 9: Introduction to Parse JavaScript SDK

Parse是什麼

Parse是一個BaaS(Backend as Service)。只要會用Parse提供的SDK, 還有正確的設定。開發者毋需擔心後端的開發撰寫以及主機的擴張和維護。

這讓開發者免於開發者實現的繁瑣細節,而將高度提升到更接近心智建模(Mental Modeling)的層級。

Page 10: Introduction to Parse JavaScript SDK

Parse開發模式概覽

Your Code

Parse SDK (黑箱)

Parse Cloud

Page 11: Introduction to Parse JavaScript SDK

知道Parse SDK怎麼用就可以寫互動式網站了(SDK還跨平台喔!)

很好很強大!

Page 12: Introduction to Parse JavaScript SDK

● localStorage:

○ jsbin.com/rubej/1/watch?html,js,output

● Parse:

○ jsbin.com/fezuq/5/watch?html,js,output

Ex: localStorage vs. Parse

Page 13: Introduction to Parse JavaScript SDK

● Data Store○ Database + File

● User Management● Background Jobs● ……

○ (see also: https://parse.com/products)

What Parse can do?

Page 14: Introduction to Parse JavaScript SDK

Website vs. Web Application

Page 15: Introduction to Parse JavaScript SDK

Website vs. Web Application

● Information Oriented vs. Action Oriented

● Creation vs. Consumption

● Way of designing

● Anything else?

● http://www.visionmobile.com/blog/2013/07/web-sites-vs-

web-apps-what-the-experts-think/

Page 16: Introduction to Parse JavaScript SDK

Class vs. Object

(雖然JavaScript不是Class-based object-oriented programming language.)

Class -> 食譜

Object (instacne) -> 菜

Class -> 藍圖

Object (instacne) -> 建築物

Page 17: Introduction to Parse JavaScript SDK

Model-View-Controller

SOURCE: http://online.stanford.edu/course/developing-ios7-apps-fall-2013

Page 18: Introduction to Parse JavaScript SDK

範例:Parse Store

Page 19: Introduction to Parse JavaScript SDK

● 模仿 ‘GetMore 二次時尚’ (其實根本抄襲)

● 二手洋裝專賣網站

● 能瀏覽商品、放入購物車

● 每個使用者有自己專屬的購物車(登入才能使

用)

● 沒有結賬功能

● http://pa4373.github.io/parsestore_js/

Parse Store

Page 20: Introduction to Parse JavaScript SDK

商品 (Dress)購物清單 (Order)使用者 (User)

Parse Store (Model)

Page 21: Introduction to Parse JavaScript SDK

Parse Store (View)

選單

產品型錄

Page 22: Introduction to Parse JavaScript SDK

Parse Store (View)

選單

產品細項

Page 23: Introduction to Parse JavaScript SDK

Parse Store (View)

選單

登入 & 註冊

Page 24: Introduction to Parse JavaScript SDK

● 版型引擎 (Template Engine)○ 解決navbar困境○ Template Tag -> 編譯-> 能產生HTML的JS函數○ 以doT.js為例

● 路由器 (Router)○ Facebook Photo○ 網址和處理函數的對應

■ ex: ‘#mycart/’ -> 處理函數1■ ‘#login/’ -> 處理函數2

○ Hash (#) vs HTML5 pushState○ Provided by Backbone.js (Parse SDK是Backbone.js的變種)

● EventListener○ 監聽特定事件的發生,觸發行為○ DOM.addEventListener(事件行為, 處理函數);○ 重複綁定?

■ https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener

Parse Store (Controller)

Page 25: Introduction to Parse JavaScript SDK

Use Parse SDK

Page 26: Introduction to Parse JavaScript SDK

Use Parse SDK

Page 27: Introduction to Parse JavaScript SDK

Use Parse SDK

記下Application Key以及JavaScript Key

Page 28: Introduction to Parse JavaScript SDK

Use Parse SDK<!doctype html>

<head>

<meta charset="utf-8">

<title>My Parse App</title>

<meta name="description" content="My Parse App">

<meta name="viewport" content="width=device-width">

<link rel="stylesheet" href="css/reset.css">

<link rel="stylesheet" href="css/styles.css">

</head>

<body>

…….

</body>

<!--匯入Parse SDK-->

<script type="text/javascript" src="http://www.parsecdn.com/js/parse-1.2.18.min.js"

></script>

<script type="text/javascript">

// 初始化SDK (把你剛剛抄下來的Application ID和 JavaScript Key放上去)

Parse.initialize("APPLICATION_ID", "JAVASCRIPT_KEY");

</script>

</html>

Page 29: Introduction to Parse JavaScript SDK

Parse App Dashboard

Analytics: App使用狀況分析Data Browser: 瀏覽儲存App的資料庫Cloud Code: Server Code (進階)Push Notifications: iOS、Android推播通知Settings: App設定

Page 30: Introduction to Parse JavaScript SDK

下載Startup Project

https://github.com/pa4373/parsestore_js/archive/startkit.zip

index.htmlcss/style.cssjs/app.js

Page 31: Introduction to Parse JavaScript SDK

Dig into HTML.<!doctype html>

…….

</body>

<!--網站各個組件的版型,包在script裏面讓template engine調用(稍後回提到)-->

<script id="loginTemplate" type="text/x-dot-template">

<div class='grid_6 prefix_3 suffix_3'>

…… </div>

</script>

<!--jQuery, required by Prase SDK-->

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>

<!--Prase SDK-->

<script src='https://www.parsecdn.com/js/parse-1.2.18.min.js'></script>

<!--doT.js, the template engine-->

<script src='./js/vendors/doT.min.js'></script>

<script src='./js/app.js'></script>

</html>

Page 32: Introduction to Parse JavaScript SDK

Dig into JavaScript. (app.js)

// 防止潛在和其他套件的衝突(function(){

初始化Parse SDK(); 將版型編譯(compile)並載入記憶體中(); 各個View相對應的處理函數(); 設定Router以及相對應的處理函數(); 初始化整個App();

})();

Page 33: Introduction to Parse JavaScript SDK

Template Engine (doT.js)● 解決navbar困境

● 編譯

○ var tempFn = doT.template("<h1>Here is a sample template {{=it.foo}}

</h1>");

○ tempFn = function(it) { var out='<h1>Here is a sample template '+(it.foo)

+'</h1>';return out; }

○ 調用doT.template是有翻譯成本的,把編譯好的存起來以供以後使用。

● 編譯在HTML裡的版型(Little DOM Magic)○ var tpl = document.getElementById("loginTemplate").text;

○ dot.template(tpl);

● 調用:var out = tempFN({foo: 'Sherlock'}); // <h1>Here is a sample template

Sherlock</h1>

○ 把它put回HTML裡面 (Little DOM Magic)

○ document.getElementById("content").innerHTML = out;

● 語法請參考:http://olado.github.io/doT/tutorial.html#intro

Page 34: Introduction to Parse JavaScript SDK

Router● linkable, bookmarkable, shareable URLs for important locations in the app● Hash vs. pushState (Why use Hash in the example?)

var App = Parse.Router.extend({

routes: {

'': 'index',

'page/:page/': 'catalog',

'dress/:dress_id/': 'dress_detail',

'mycart/': 'mycart',

'login/*redirect': 'login',

},

// If frontpage is requested, show the first page of catalog.

index: function(){

return handlers.catalog(1);

},

catalog: handlers.catalog,

dress_detail: handlers.dress_detail,

mycart: handlers.mycart,

login: handlers.login,

});

路徑規則和相對應的處理函數(從物件的其他方法找查)

處理函數名稱以及函數本體(匿名宣告 or 參照)

:page -> 參數 function catalog(page)(*redirect也是另外一種參數)

Ref: http://backbonejs.org/#Router

Page 35: Introduction to Parse JavaScript SDK

Router● 當訪問#page/1/發生了什麼事呢?

var App = Parse.Router.extend({

routes: {

'': 'index',

'page/:page/': 'catalog',

'dress/:dress_id/': 'dress_detail',

'mycart/': 'mycart',

'login/*redirect': 'login',

},

// If frontpage is requested, show the first page of catalog.

index: function(){

return handlers.catalog(1);

},

catalog: handlers.catalog,

dress_detail: handlers.dress_detail,

mycart: handlers.mycart,

login: handlers.login,

});

路徑匹配

呼叫handlers.catalog(1)函數。(1 = :page)

Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window.onhashchange

Page 36: Introduction to Parse JavaScript SDK

Router● 讓Router活起來。

// this = windowthis.Router = new App();Parse.history.start();

Page 37: Introduction to Parse JavaScript SDK

Handler Function

var handlers = { A: function(){}, B: function(){}, C: function(){},};

vs.var handlerA = function(){};var handlerB = function(){};var handlerC = function(){};

Page 38: Introduction to Parse JavaScript SDK

Handler Function與Router相關:index: function(){

return handlers.catalog(1);

}, -> 視同瀏覽產品型錄第一頁

catalog: handlers.catalog, -> 顯示產品型錄

dress_detail: handlers.dress_detail, -> 顯示商品細項

mycart: handlers.mycart, -> 顯示購物車

login: handlers.login, -> 顯示

與Router無關:

navbar -> 根據使用者登入與否顯示navbar內容

Page 39: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)

if (登入了){ 重新導向到首頁();} else { 印出登入+註冊版型(); 綁定登入按鈕觸發事件(); // Parse User Object

綁定兩次密碼一致與否檢查事件(); 綁定註冊按鈕觸發事件(); // Parse User Object

}

Page 40: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)

// 綁定登入按鈕觸發事件();document.getElementById('loginForm').addEventListener('submit', function(){

Parse.User.logIn(document.getElementById('loginForm_username').value,

document.getElementById('loginForm_password').value, {

success: function(user) {

// Do stuff after successful login

postAction();

}, error: function(user, error) {

// The login failed. Check error to see why.

}

});

});

/* Parse.User : Parse SDK提供的User物件,讓開發者可以簡便的建立會員機制

* Parse.User.logIn(帳號, 密碼,

* {success: 登入成功的回調函數, error: 登入失敗的回調函數});

* 登入成功後,會在瀏覽器裡面留下session cookie, 可以透過Parse SDK調用的函數。

*/

Page 41: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)// 還記得這段語法嗎?

var currentUser = Parse.User.current();

if (currentUser) {

...

}else{

...

}

/* 如果使用者有登入的話,Parse.User.current()會回傳現今登入的

* 使用者物件,透過檢查物件物件的存在,我們能夠設計需要登入的函數。

*/

Page 42: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)

// 綁定兩次密碼一致與否檢查事件();document.getElementById('singupForm_password1').

addEventListener('keyup', function(){

// 動態抓密碼欄的值 (Why?)

var singupForm_password = document.getElementById('singupForm_password');

var message = (this.value !== singupForm_password.value) ? '密碼不一致,請再

確認一次。' : '';

document.getElementById('signupForm_message').innerHTML = message;

});

Page 43: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)// 綁定註冊按鈕觸發事件();document.getElementById('singupForm').addEventListener('submit', function(){

var user = new Parse.User();

user.set("username", document.getElementById('singupForm_username').value);

user.set("password", document.getElementById('singupForm_password').value);

user.set("email", document.getElementById('singupForm_emailAddress').value);

user.signUp(null, {

success: function(user) {

postAction();

// Hooray! Let them use the app now.

},

error: function(user, error) {

// Show the error message somewhere and let the user try again.

document.getElementById('signupForm_message').innerHTML =

error.message + '['+error.code+']';

}

});

}, false);

Page 44: Introduction to Parse JavaScript SDK

Handler Function (Login / Signup)// 綁定註冊按鈕觸發事件();// 在本地創建一個User物件

var user = new Parse.User();

// 設定帳號密碼電子郵件

user.set("username", 帳號);

user.set("password", 密碼);

user.set("email", 電子郵件地址);

/* 註冊一個新的使用者並直接登入(不用做兩次!)

* 第一個null是啥?

* Extra fields to set on the new user, or null.*/

user.signUp(null, {success: 登入成功的回調函數, error: 登入失敗的回調函數});

Page 45: Introduction to Parse JavaScript SDK

Handler Function (Catalog)

移動到文件最上方 // 按next時會怎麼樣?

設定分頁參數(); // pagination = skip + limit;

設定查詢參數(); // Parse Query

查詢Parse伺服器資料庫(); // 取回物件列表

印出產品型錄版型();設定查詢參數(); // 解除所有限制

印出分頁版型(); // Parse Dress Object (為什麼晚查?)

// 因為分頁版型要加附的DOM在型錄版型內

Page 46: Introduction to Parse JavaScript SDK

Handler Function (Catalog)var handler = function(page){

// page意指現今頁數

window.scrollTo(0,0); // 移動到文件最上方

var limit = 16; // 每頁顯示多少筆資料

var skip = (page-1) * limit; // 要略過多少筆之前的資料

var Dress = Parse.Object.extend("Dress"); // 取得Parse的Dress class

var query = new Parse.Query(Dress); // 創建一個找查Dress的Query物件

query.limit(limit); // 設定Query條件

query.skip(skip);

query.descending("createdAt"); // 按照創造時間降冪排序

// 執行query (網路連結直到此處才會觸發)

query.find({success: function(results){

// 處理回傳的結果,results變數指向回傳的物件列表

...

}});

}

Page 47: Introduction to Parse JavaScript SDK

Handler Function (Catalog){success: function(results){

var objList = results.map(function(e){ return e.toJSON() }); // 將物件列表轉化成版型能消化的格式 document.getElementById('content').innerHTML =

templates.catalogTemplate(objList); // 呼叫型錄的模板函數。

query.limit(0);

query.skip(0); // 設成0, 我們才能找到所有Dress object總數

var option = {};

query.count({success: function(count){

var totalPage = Math.ceil(count / limit); // Math.celi(3.1415926) = 4;

var currentPage = parseInt(page); // 轉型( string => int )

option = {

// Watch out the limit.

'previous': (currentPage === 1) ? 1 : currentPage-1,

'next': (currentPage === totalPage) ? currentPage : currentPage+1, // 不可以超過最前最後頁

'current': currentPage,

'last': totalPage,

};

document.getElementById('pagination').innerHTML =

templates.catalogPaginationTemplate(option); // 呼叫分頁的模板函數。

}, error: function(err){}

});

}

Page 48: Introduction to Parse JavaScript SDK

Handler Function (Dress Detail)

if(有洋裝Id參數){ 設定查詢參數(); // Parse Query

查詢Parse伺服器資料庫(); // 取回物件內容

印出產品細則版型(); 綁定加入購物車功能(); // Parse Relational Object

} else { 重新導向到首頁();}

Page 49: Introduction to Parse JavaScript SDK

Handler Function (Dress Detail)var handler = function(dress_id){

if(dress_id){

var Dress = Parse.Object.extend("Dress"); // 取得Parse的Dress class

var query = new Parse.Query(Dress); // 創建一個找查Dress的Query物件

query.get(dress_id, { // 執行query,注意get方法 -> 給定物件ID, 回傳物件 success: function(dress){

document.getElementById('content').innerHTML =

templates.dress_detialTemplate(dress.toJSON());

綁定加入購物車功能(); // 下一張slide會解釋

}, error: function(object, error){

}

});

} else {

window.location.hash = '';

}

}

Page 50: Introduction to Parse JavaScript SDK

Parse Relational Object

var John = { ‘height’: 180, ‘girlfriend’: Jenny}What is the relationship between John and Jenny?How tall is John’s girlfriend? John.girlfriend.height = 167

Why? Data Consistency.

var Jenny = { ‘height’: 167,}

Page 51: Introduction to Parse JavaScript SDK

Parse Relational Object

var order = { ‘user’: <User obj>, ‘dress’: <Dress obj>, ‘amount’: 10,}

Page 52: Introduction to Parse JavaScript SDK

Handler Function (Dress Detail)document.getElementById('addToCart').addEventListener('click', function(){

var currentUser = Parse.User.current(); // 檢查登入

if(currentUser){

var e = document.getElementById('amount');

var amount = parseInt(e.options[e.selectedIndex].value);

myCart.setAmountTo(currentUser, dress, amount, function(){

alert("此商品已加入到您的購物車。 ");

}); // 下一張slide會解釋

} else {

// 重新導向到登入頁面,登入後會回到商品

window.location.hash = 'login/'+ window.location.hash;

}

});

Page 53: Introduction to Parse JavaScript SDK

myCart.setAmoutTomyCart = {

setAmountTo: function(user, dress, amount, callback){

var Order = Parse.Object.extend("Order"); // 取得Parse的Order class

// 創建一個找查Order的Query物件

var query = new Parse.Query(Order);

// 設定Query條件(object的user欄位指向到給定的User object)

query.equalTo('user', user);

// 設定Query條件(object的dress欄位指向到給定的Dress object)

query.equalTo('dress', dress);

// 執行query,注意first方法 -> 給定物件ID, 回傳物件列表第一項(可能會沒有)

query.first({success: 查詢成功的回調函數, error: 查詢失敗的回調函數});

},

};

/*

* myCart.setAmountTo(User物件, Dress物件, 數量(int), 回調函數);

*/

Page 54: Introduction to Parse JavaScript SDK

myCart.setAmoutTo{

success: function(order){

if( amount === 0 && order ){ // 如果已經有存在的order,並收到將數量設成0的話,等於消滅order物件

order.destroy({ // 消滅Parse物件

success: function(order){

callback(); //調用當作參數的callback函數

}

});

} else {

if( order === undefined ){ // 如果order還不存在,創一個新的object

order = new Order();

order.set('user', user); // 指定新object的user欄位指向到給定的Dress object

order.set('dress', dress); // 指定新object的dress欄位指向到給定的Dress object

}

order.set('amount', amount);

order.save(null, { // 將新增或更改過的order object 存到Parse Server

success: function(order){

callback();

}

});

}

}, error: function(object, err){

}

}

Page 55: Introduction to Parse JavaScript SDK

Handler Function (My Cart)

if (登入了){ 設定查詢參數(); // Parse Query for Order 查詢Parse伺服器資料庫(); // 取回物件內容

迴圈印出各訂單並綁上修改數量和刪除的事件();} else { 重新導向到首頁();}

Page 56: Introduction to Parse JavaScript SDK

Handler Function (My Cart)mycart: function(){

var currentUser = Parse.User.current();

if (currentUser) {

var Order = Parse.Object.extend("Order");

var query = new Parse.Query(Order);

query.equalTo('user', currentUser);

query.include('dress');

query.find({success: 登入成功的回調函數, error: 登入失敗的回調函數});

} else {

window.location.hash = 'login/'+ window.location.hash;

}

}

Page 57: Introduction to Parse JavaScript SDK

Handler Function (My Cart){

success: function (results) {

var objList = results.map(function (e) {

return {

'dressId': e.get('dress').id,

'amount': e.get('amount'),

'name': e.get('dress').get('name'),

'previewUrl': e.get('dress').get('previewUrl'),

}

});

document.getElementById('content').innerHTML = templates.mycartTemplate(objList);

results.forEach(function (e) {

var changeAmount = document.getElementById('change_amount_' + e.get('dress').id);

changeAmount.addEventListener('change', function () {

var amount = parseInt(this.options[this.selectedIndex].value);

myCart.setAmountTo(currentUser, e.get('dress'), amount, function () {});

});

var cancelOrderBtn = document.getElementById('cancel_order_' + e.get('dress').id);

cancelOrderBtn.addEventListener('click', function () {

myCart.setAmountTo(currentUser, e.get('dress'), 0, function () {

if (cancelOrderBtn.parentNode.parentNode.childElementCount === 1) {

handlers.mycart();

} else {

cancelOrderBtn.parentNode.remove();

}

});

});

});

document.getElementById('payButton').parentNode.addEventListener('click', function () {

alert('沒做這功能喔');

});

}, error: function (error){ },

}

Page 58: Introduction to Parse JavaScript SDK

Handler Function

// See the pattern?function(){ 預處理(); // ex: 檢查登入狀況

載入模型(); // optional 使用樣板引擎將模型顯示到browser上(); 事件綁定(); // Event binding (eg. click)};

註:這樣的設計只是參考不是絕對,應按照合理的情況去撰寫相對應的程序

Page 59: Introduction to Parse JavaScript SDK

Privilege Issues

How to protect data?

Page 60: Introduction to Parse JavaScript SDK

Parse Class-Based Privilege (Data Browser)

Ref: https://parse.com/docs/data#security-classes

Page 61: Introduction to Parse JavaScript SDK

Parse Class-Based Privilege (Data Browser)

Page 62: Introduction to Parse JavaScript SDK

Parse ACL (more complicated!)

ACL: Access Control List“...each object has a list of users and roles along with what permissions that user or role has...”

user vs. roles

Ref: https://parse.com/docs/data#security-objects

Page 63: Introduction to Parse JavaScript SDK

Parse ACL

{ "*":{"read":true}, "SaMpLeUsErId":{"write":true,"read":true} }

SaMpLeUsErId 這個user可以讀寫這個物件

其他人只能讀

Page 64: Introduction to Parse JavaScript SDK

Parse ACL

How to make the certain ‘Order’ object available only to the owner?

var orderACL = new Parse.ACL();orderACL.setPublicReadAccess(false);orderACL.setPublicWriteAccess(false);orderACL.setReadAccess(user, true);orderACL.setWriteAccess(user, true);// 附加到物件實體(instance)上order.setACL(postACL);order.save();

Ref: http://parse.com/docs/js/symbols/Parse.ACL.html

Page 65: Introduction to Parse JavaScript SDK

Parse ACL

Page 66: Introduction to Parse JavaScript SDK

Parse Store

All Source codes are available on GitHub:https://github.com/pa4373/parsestore_js

using git to clone!

$ git clone https://github.com/pa4373/parsestore_js.git

Page 67: Introduction to Parse JavaScript SDK

More Topics…...

● Parse JavaScript Tutorial● Parse JavaScript SDK Reference● Pricing● Loading indicator● Backbone.js

○ Data-Binding