Your SlideShare is downloading. ×
Introduction to Parse JavaScript SDK
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Introduction to Parse JavaScript SDK

510

Published on

Published in: Engineering
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
510
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
8
Comments
0
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. 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)
  • 2. So far,你的網頁都是一成不變的 http://www.pmichaud.com/toast/ (從1994年後都沒變過)
  • 3. 但實際上,很多網站是隨時在改變的
  • 4. 為什麼網站可以不斷的、及時變化? 因為有後端啊。 *Web伺服器:接收http請求、產生或委派http回應並傳回client 端 *應用程式伺服器:根據使用者的請求產生相對的回應 資料庫:像一張巨大的Excel表,存各地來的資料
  • 5. Backend Technology Stack
  • 6. What if? 如果有一個工具可以讓我不用寫後端程式碼,但 還是可以使用後端的功能,該有多好啊......
  • 7. Parse來拯救咧!
  • 8. Parse是什麼 Parse是一個BaaS(Backend as Service)。 只要會用Parse提供的SDK, 還有正確的設定。開發者毋需擔 心後端的開發撰寫以及主機的擴張和維護。 這讓開發者免於開發者實現的繁瑣細節,而將高度提升到更 接近心智建模(Mental Modeling)的層級。
  • 9. Parse開發模式概覽 Your Code Parse SDK (黑箱) Parse Cloud
  • 10. 知道Parse SDK怎麼用就可以寫互動式網站 了(SDK還跨平台喔!) 很好很強大!
  • 11. ● localStorage: ○ jsbin.com/rubej/1/watch?html,js,output ● Parse: ○ jsbin.com/fezuq/5/watch?html,js,output Ex: localStorage vs. Parse
  • 12. ● Data Store ○ Database + File ● User Management ● Background Jobs ● …… ○ (see also: https://parse.com/products) What Parse can do?
  • 13. Website vs. Web Application
  • 14. 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/
  • 15. Class vs. Object (雖然JavaScript不是Class-based object- oriented programming language.) Class -> 食譜 Object (instacne) -> 菜 Class -> 藍圖 Object (instacne) -> 建築物
  • 16. Model-View-Controller SOURCE: http://online.stanford.edu/course/developing-ios7-apps-fall-2013
  • 17. 範例:Parse Store
  • 18. ● 模仿 ‘GetMore 二次時尚’ (其實根本抄襲) ● 二手洋裝專賣網站 ● 能瀏覽商品、放入購物車 ● 每個使用者有自己專屬的購物車(登入才能使 用) ● 沒有結賬功能 ● http://pa4373.github.io/parsestore_js/ Parse Store
  • 19. 商品 (Dress) 購物清單 (Order) 使用者 (User) Parse Store (Model)
  • 20. Parse Store (View) 選單 產品型錄
  • 21. Parse Store (View) 選單 產品細項
  • 22. Parse Store (View) 選單 登入 & 註冊
  • 23. ● 版型引擎 (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)
  • 24. Use Parse SDK
  • 25. Use Parse SDK
  • 26. Use Parse SDK 記下Application Key以及JavaScript Key
  • 27. 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>
  • 28. Parse App Dashboard Analytics: App使用狀況分析 Data Browser: 瀏覽儲存App的資料庫 Cloud Code: Server Code (進階) Push Notifications: iOS、Android推播通知 Settings: App設定
  • 29. 下載Startup Project https://github. com/pa4373/parsestore_js/archive/startkit.zip index.html css/style.css js/app.js
  • 30. 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>
  • 31. Dig into JavaScript. (app.js) // 防止潛在和其他套件的衝突 (function(){ 初始化Parse SDK(); 將版型編譯(compile)並載入記憶體中(); 各個View相對應的處理函數(); 設定Router以及相對應的處理函數(); 初始化整個App(); })();
  • 32. 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
  • 33. 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
  • 34. 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
  • 35. Router ● 讓Router活起來。 // this = window this.Router = new App(); Parse.history.start();
  • 36. Handler Function var handlers = { A: function(){}, B: function(){}, C: function(){}, }; vs. var handlerA = function(){}; var handlerB = function(){}; var handlerC = function(){};
  • 37. 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內容
  • 38. Handler Function (Login / Signup) if (登入了){ 重新導向到首頁(); } else { 印出登入+註冊版型(); 綁定登入按鈕觸發事件(); // Parse User Object 綁定兩次密碼一致與否檢查事件(); 綁定註冊按鈕觸發事件(); // Parse User Object }
  • 39. 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調用的函數。 */
  • 40. Handler Function (Login / Signup) // 還記得這段語法嗎? var currentUser = Parse.User.current(); if (currentUser) { ... }else{ ... } /* 如果使用者有登入的話,Parse.User.current()會回傳現今登入的 * 使用者物件,透過檢查物件物件的存在,我們能夠設計需要登入的函數。 */
  • 41. 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; });
  • 42. 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);
  • 43. 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: 登入失敗的回調函數});
  • 44. Handler Function (Catalog) 移動到文件最上方 // 按next時會怎麼樣? 設定分頁參數(); // pagination = skip + limit; 設定查詢參數(); // Parse Query 查詢Parse伺服器資料庫(); // 取回物件列表 印出產品型錄版型(); 設定查詢參數(); // 解除所有限制 印出分頁版型(); // Parse Dress Object (為什麼晚查?) // 因為分頁版型要加附的DOM在型錄版型內
  • 45. 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變數指向回傳的物件列表 ... }}); }
  • 46. 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){} }); }
  • 47. Handler Function (Dress Detail) if(有洋裝Id參數){ 設定查詢參數(); // Parse Query 查詢Parse伺服器資料庫(); // 取回物件內容 印出產品細則版型(); 綁定加入購物車功能(); // Parse Relational Object } else { 重新導向到首頁(); }
  • 48. 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 = ''; } }
  • 49. 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, }
  • 50. Parse Relational Object var order = { ‘user’: <User obj>, ‘dress’: <Dress obj>, ‘amount’: 10, }
  • 51. 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; } });
  • 52. myCart.setAmoutTo myCart = { 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), 回調函數); */
  • 53. 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){ } }
  • 54. Handler Function (My Cart) if (登入了){ 設定查詢參數(); // Parse Query for Order 查詢Parse伺服器資料庫(); // 取回物件內容 迴圈印出各訂單並綁上修改數量和刪除的事件(); } else { 重新導向到首頁(); }
  • 55. 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; } }
  • 56. 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){ }, }
  • 57. Handler Function // See the pattern? function(){ 預處理(); // ex: 檢查登入狀況 載入模型(); // optional 使用樣板引擎將模型顯示到browser上(); 事件綁定(); // Event binding (eg. click) }; 註:這樣的設計只是參考不是絕對,應按照合理的情況去撰寫 相對應的程序
  • 58. Privilege Issues How to protect data?
  • 59. Parse Class-Based Privilege (Data Browser) Ref: https://parse.com/docs/data#security-classes
  • 60. Parse Class-Based Privilege (Data Browser)
  • 61. 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
  • 62. Parse ACL { "*":{"read":true}, "SaMpLeUsErId":{"write": true,"read":true} } SaMpLeUsErId 這個user可以讀寫這個物件 其他人只能讀
  • 63. 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
  • 64. Parse ACL
  • 65. 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
  • 66. More Topics…... ● Parse JavaScript Tutorial ● Parse JavaScript SDK Reference ● Pricing ● Loading indicator ● Backbone.js ○ Data-Binding

×