摘要:我們將使用方法創建一個。我們傳遞一個布爾類型,這個就是我們早先討論的的參數。再使用和構建博客教程系列的第三部分見。所有的源碼都在上,但是應用程序的源碼還沒有放上去,因為我們還沒有完成它,等到第三部分寫完以后再放全部的源碼到上。
注:該文作者是 John Kevin M. Basco,原文地址是 Building a blog using Flask and AngularJS Part 2
注:翻譯的第一部分請移步到 - 使用 Flask 和 AngularJS 構建博客 - 1
這是這個教程系列的第二部分,如果你還沒有都第一部分,請移步到這里:http://blog.john.mayonvolcanosoftware.com/building-a-blog-using-flask-and-angularjs-part-1/
因為我們在該系列的第一部分已經構建好了 REST API ,在這部分我們將專注于構建一個 AngularJS 應用,用來使用我們構建的 REST API。
目錄結構AngularJS 應用的目錄結構看起來像這樣:
安裝必要的包我們將使用的包如下:
angular-route
bootstrap
restangular
angularjs
angular-local-storage
如果你不熟悉 Restangular 的話,我建議你首先讀下 Restangular 的文檔 - https://github.com/mgonto/restangular
或者如果你懶得讀它的文檔,隨時繼續閱讀本教程,當你遇到如下對你沒有意義的 Restangular 代碼的時候,你可以參考下 Restangular 文檔。
為了使得事情更容易,在 blog/client/ 目錄創建一個 bower.json 文件,并且拷貝和粘貼以下內容:
{ "name": "client", "version": "0.0.0", "authors": [ "John Kevin Basco" ], "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ], "dependencies": { "angular-route": "~1.2.21", "bootstrap": "~3.2.0", "restangular": "~1.4.0", "angularjs": "~1.2.21", "angular-local-storage": "~0.0.7" } }
然后進入 blog/client 目錄,運行 bower install 來安裝必要的包。
初始化應用設置首先,在 blog/client 目錄下創建一個新文件,并命名為 index.html,然后拷貝和粘貼以下內容:
Blog
在以上代碼中,我們只是簡單的包含了我們稍后將要創建的 css 和 javascript 文件。我們也為 navigation bar, footer 等等添加了 html markup。你將注意到在以上的 gist 中,它包含了 這個 html 元素。這是一個當前的路由將用來插入模板的地方。你將看到這些事情:ng-class="{active: isActive("/")}" 和 ng-show="isLoggedIn",忽略它們現在。我們將在 為 ApplicationCtrl 寫代碼的時候討論它們。
添加一些樣式現在讓我們添加一些樣式以便這個 app 看起來更好。為了使我們的工作更輕松,讓我們使用一個 Bootstrap 提供的主題 - http://getbootstrap.com/examples/blog/。在 app/client/css 目錄下創建一個新文件,并命名為 theme.css,然后拷貝和粘貼以下內容:
/* * Globals */ body { font-family: Georgia, "Times New Roman", Times, serif; color: #555; } h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 { margin-top: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: normal; color: #333; } /* * Override Bootstrap"s default container. */ @media (min-width: 1200px) { .container { width: 970px; } } /* * Masthead for nav */ .blog-masthead { background-color: #428bca; -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); } /* Nav links */ .blog-nav-item { position: relative; display: inline-block; padding: 10px; font-weight: 500; color: #cffffdeb; } .blog-nav-item:hover, .blog-nav-item:focus { color: #fff; text-decoration: none; } /* Active state gets a caret at the bottom */ .blog-nav .active { color: #fff; } .blog-nav .active:after { position: absolute; bottom: 0; left: 50%; width: 0; height: 0; margin-left: -5px; vertical-align: middle; content: " "; border-right: 5px solid transparent; border-bottom: 5px solid; border-left: 5px solid transparent; } /* * Blog name and description */ .blog-header { padding-top: 20px; padding-bottom: 20px; } .blog-title { margin-top: 30px; margin-bottom: 0; font-size: 60px; font-weight: normal; } .blog-description { font-size: 20px; color: #999; } /* * Main column and sidebar layout */ .blog-main { font-size: 18px; line-height: 1.5; } /* Sidebar modules for boxing content */ .sidebar-module { padding: 15px; margin: 0 -15px 15px; } .sidebar-module-inset { padding: 15px; background-color: #f5f5f5; border-radius: 4px; } .sidebar-module-inset p:last-child, .sidebar-module-inset ul:last-child, .sidebar-module-inset ol:last-child { margin-bottom: 0; } /* Pagination */ .pager { margin-bottom: 60px; text-align: left; } .pager > li > a { width: 140px; padding: 10px 20px; text-align: center; border-radius: 30px; } /* * Blog posts */ .blog-post { margin-bottom: 60px; } .blog-post-title { margin-bottom: 5px; font-size: 40px; } .blog-post-meta { margin-bottom: 20px; color: #999; } /* * Footer */ .blog-footer { padding: 40px 0; color: #999; text-align: center; background-color: #f9f9f9; border-top: 1px solid #e5e5e5; } .blog-footer p:last-child { margin-bottom: 0; }
在 app/client/css 目錄下創建另外一個文件,命名為 styles.css ,然后拷貝和粘貼一下內容:
.main-view { margin-top: 20px; margin-bottom: 20px; }
現在進入 blog/client 目錄,然后運行這個命令:python -m SimpleHTTPServer 8000,現在訪問 http://localhost:8000/,你應該可以看到應用已經有設計樣式。
創建必須的 javascript 文件注意:以下應用使用的配置變量的值,為了假話,被硬編碼了。在生產環境的應用中,需要把它放入一個配置文件中,因此你可以根據不同的環境使用不同的值,比如開發環境和生產環境。
現在讓我們創建必須的 javascript 文件,在 blog/client/js 目錄下創建一個新文件,命名為 main.js 并且以下的代碼拷貝和粘貼進文件:
window.Blog = angular.module("Blog", ["ngRoute", "restangular", "LocalStorageModule"]) .run(function($location, Restangular, AuthService) { Restangular.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) { headers["Authorization"] = "Basic " + AuthService.getToken(); return { headers: headers }; }); Restangular.setErrorInterceptor(function(response, deferred, responseHandler) { if (response.config.bypassErrorInterceptor) { return true; } else { switch (response.status) { case 401: AuthService.logout(); $location.path("/sessions/create"); break; default: throw new Error("No handler for status code " + response.status); } return false; } }); }) .config(function($routeProvider, RestangularProvider) { RestangularProvider.setBaseUrl("http://localhost:5000/api/v1"); var partialsDir = "../partials"; var redirectIfAuthenticated = function(route) { return function($location, $q, AuthService) { var deferred = $q.defer(); if (AuthService.isAuthenticated()) { deferred.reject() $location.path(route); } else { deferred.resolve() } return deferred.promise; } } var redirectIfNotAuthenticated = function(route) { return function($location, $q, AuthService) { var deferred = $q.defer(); if (! AuthService.isAuthenticated()) { deferred.reject() $location.path(route); } else { deferred.resolve() } return deferred.promise; } } $routeProvider .when("/", { controller: "HomeDetailCtrl", templateUrl: partialsDir + "/home/detail.html" }) .when("/sessions/create", { controller: "SessionCreateCtrl", templateUrl: partialsDir + "/session/create.html", resolve: { redirectIfAuthenticated: redirectIfAuthenticated("/posts/create") } }) .when("/sessions/destroy", { controller: "SessionDestroyCtrl", templateUrl: partialsDir + "/session/destroy.html" }) .when("/users/create", { controller: "UserCreateCtrl", templateUrl: partialsDir + "/user/create.html" }) .when("/posts/create", { controller: "PostCreateCtrl", templateUrl: partialsDir + "/post/create.html", resolve: { redirectIfNotAuthenticated: redirectIfNotAuthenticated("/sessions/create") } }); })
在以上代碼中,我們初始化了我們的應用,配置 Restangular 來包括每個請求的登錄用戶的用戶名和密碼,配置 Restangular 來使用一個錯誤攔截器,它將刪除本地存儲的所有已經保存的數據,并且如果 REST API 返回一個 401 狀態碼,則把用戶跳轉到登錄頁面。
在配置部分,我們設置了 Restangular 將使用的基準 url,我們也設置了路由。你將注意到 /posts/create 路由有一個解決屬性包含了 redirectIfNotAuthenticated promise。是的,像你猜的那樣,如果用戶未被授權,它將把用戶跳轉到登錄頁面。/sessions/create 路由也有一個解決屬性,但是它包含了 redirectIfAuthenticated promise 而不是 redirectIfNotAuthenticated promise。如果用戶被授權了,它將簡單的跳轉用戶到創建博客的頁面。你可以閱讀關于這個主題的更多信息 - http://blog.john.mayonvolcanosoftware.com/protecting-routes-in-angularjs/。
Factories讓我們創建我們的應用將使用到的工廠方法。
Post讓我們在 blog/client/js/factories 目錄下創建一個新文件,命名為 Post.js ,然后拷貝并粘貼一下代碼到文件中:
Blog.factory("Post", function(Restangular) { var Post; Post = { get: function() { return Restangular .one("posts") .getList(); }, create: function(data) { return Restangular .one("posts") .customPOST(data); } }; return Post; })
在以上代碼中,factory 將創建一個對象,該對象有一個 get 和 create 方法。我們將使用 get 方法來獲取博客列表,create 方法來創建一篇博客。
Session讓我們在 blog/client/js/factories 目錄下創建一個新文件,命名為 Session.js ,然后拷貝并粘貼一下代碼到文件中:
Blog.factory("Session", function(Restangular) { var Session; Session = { create: function(data, bypassErrorInterceptor) { return Restangular .one("sessions") .withHttpConfig({bypassErrorInterceptor: bypassErrorInterceptor}) .customPOST(data); } }; return Session; })
上面的 Session 工廠有一個 create 方法返回這個對象。我們將用這個 create 方法來授權一個用戶的 email 和密碼。你將注意到 create 方法有一個參數叫做 bypassErrorInterceptor。這個參數應該是一個布爾類型的值(true or false),當 bypassErrorInterceptor 是 true 的時候,它將繞過我們早先在配置塊中定義的 error 攔截器。這不會立即顯得有意義,但是在后面我們將遇到一個場景,在那我們需要繞過 error 攔截器。
User在 blog/client/js/factories 目錄下創建一個新文件,并命名為 User.js,然后把以下代碼拷貝和粘貼進文件:
Blog.factory("User", function(Restangular) { var User; User = { create: function(user) { return Restangular .one("users") .customPOST(user); } }; return User; }
User 工廠將有一個 create 方法返回該對象。我們將使用 create 方法創建一個 user。
Services我們的應用需要一個服務,該服務包含登錄,登出,和校驗該用戶是否被授權的業務邏輯。我們把該服務命名為 AuthService。在 blog/cient/js/services 目錄下創建一個新文件,并命名為 AuthService.js,然后拷貝和粘貼一下代碼到文件中:
Blog.service("AuthService", AuthService = function($q, localStorageService, Session) { this.login = function(credentials) { var me = this; deferred = $q.defer() Session.create(credentials, true).then(function(user) { me.setToken(credentials); return deferred.resolve(user); }, function(response) { if (response.status == 401) { return deferred.reject(false); } throw new Error("No handler for status code " + response.status); }); return deferred.promise }; this.logout = function() { localStorageService.clearAll(); }; this.isAuthenticated = function() { var token = this.getToken(); if (token) { return true } return false; }; this.setToken = function(credentials) { localStorageService.set("token", btoa(credentials.email + ":" + credentials.password)); }; this.getToken = function() { return localStorageService.get("token"); }; return this; }
以上的 login 方法使用 Session.create 認證用戶的認證信息。當登錄認證信息是有效的,我們將保存 base64 編碼格式的 email 和密碼到本地存儲稍后使用,并授權。當登錄認證信息是無效的(當登錄認證信息是無效的,REST API 將返回一個 401 狀態碼),我們簡單的拒絕授權,并且讓使用該方法的代碼處理它。看一下我們傳遞給 Session.create 的第二個參數。我們傳遞一個布爾類型(true),這個就是我們早先討論的 bypassErrorInterceptor 的參數。我們需要繞過在配置塊中的 error 攔截器,因為如果我們不這樣做,處理 401 錯誤碼的 handler 將把用戶跳轉到登錄視圖。我們想這個發生在其他場景,但當我們正在登錄視圖并且用戶提交了錯誤的認證信息的時候,我們想展示一個錯誤消息,比如“Incorrect login credentials. Please try again.”。這就是為什么我們需要繞過默認的 error 攔截器,使用一個新的,包含了不同的業務邏輯(檢查以上代碼的 10 - 15 行)。
logout 方法將清理保存在本地存儲的數據,isAuthenticated 方法僅僅通過檢查 token 是否被呈現來檢查當前用戶是否被授權。setToken 方法保存一個 token (base64 編碼格式的 email 和 password)到本地存儲中,getToken 方法獲取保存在本地存儲中的 token。
Directives我們應用的 registration form 將有一個 password 和 一個確認 password 字段,因此我們需要一個方法來校驗值是否匹配。為這個我們將使用 directive,在 blog/client/js/directives 目錄下創建一個新文件,并命名為 match.js,然后拷貝和粘貼一下代碼到文件中:
Blog.directive("match", function () { return { require: "ngModel", restrict: "A", scope: { match: "=" }, link: function(scope, elem, attrs, ctrl) { scope.$watch(function() { return (ctrl.$pristine && angular.isUndefined(ctrl.$modelValue)) || scope.match === ctrl.$modelValue; }, function(currentValue) { ctrl.$setValidity("match", currentValue); }); } }; }
信任以上 directive 的作者。我是從別處抓取來了以上源碼,但是我沒有忘記鏈接到該站點和誰是作者。
我們現在完成了我們 AngularJS 應用程序的一半。我們還沒有寫的剩下部分是控制層和模板。因為這篇文章太長了,我決定在第三部分寫控制層和模板部分。同時,研究目前為止我們已經編寫的代碼,讓我們更熟悉它。再使用 Flask 和 AngularJS 構建博客教程系列的第三部分見。
所有的 REST API 源碼都在 Github 上 - https://github.com/basco-johnkevin/building-a-blog-using-flask-and-angularjs ,但是 AngularJS 應用程序的源碼還沒有放上去,因為我們還沒有完成它,等到第三部分寫完以后再放全部的源碼到 Github 上。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/45297.html
摘要:注原文作者,原文地址為在這個教程中,我們將使用和構建一個博客。在開發期間,這將允許我們把它們運行在不同的端口例如和。現在我們將進入目錄并使用運行這個腳本。示例創建一篇文章為了創建一篇文章,你需要發送一個請求給。 注:原文作者 John Kevin M. Basco,原文地址為 Building a blog using Flask and AngularJS Part 1 在...
摘要:注原文作者,原文地址為在這個教程中,我們將使用和構建一個博客。在開發期間,這將允許我們把它們運行在不同的端口例如和。現在我們將進入目錄并使用運行這個腳本。示例創建一篇文章為了創建一篇文章,你需要發送一個請求給。 注:原文作者 John Kevin M. Basco,原文地址為 Building a blog using Flask and AngularJS Part 1 在...
摘要:推薦閱讀資源庫工具應用程序精選列表中文版有哪些鮮為人知,但是很有意思的網站一份攻城獅筆記每天搜集上優秀的項目一些有趣的民間故事超好用的谷歌瀏覽器油猴插件合集目錄資源文檔文章圖書會談教程更多庫工具管理數據部署桌面發展監控應用資源文檔介紹文檔教 推薦閱讀 MongoDB 資源、庫、工具、應用程序精選列表中文版 有哪些鮮為人知,但是很有意思的網站? 一份攻城獅筆記 每天搜集 Github ...
摘要:下一篇譯精通使用開發二原版書名第一章之道這一章主要是介紹,包括這個框架以及它背后的項目。幸運的是,擁有一個活躍的,支持度高的社區。另外,社區還為已經存在的工具箱里貢獻了許多有意思的工具。 下一篇:【譯】《精通使用AngularJS開發Web App》(二) 原版書名:Mastering Web Application Development with AngularJS Ch...
摘要:在被收購之后,維護并繼續發展。設置是告訴應用在目錄尋找應用模板。設置告訴應用使用目錄里面的類似圖像文件等靜態文件。我們會在應用開發過程中,保持著調試器在后臺運行。這能提供高效的開發環境。我們會把回應狀態設為已創建。 編者注:我們發現了有趣的系列文章《30天學習30種新技術》,正在翻譯,一天一篇更新,年終禮包。下面是第23天的內容。 今天的《30天學習30種新技術》,我決定暫時放下...
閱讀 899·2021-10-25 09:44
閱讀 1272·2021-09-23 11:56
閱讀 1194·2021-09-10 10:50
閱讀 3140·2019-08-30 15:53
閱讀 2143·2019-08-30 13:17
閱讀 626·2019-08-29 18:43
閱讀 2501·2019-08-29 12:57
閱讀 862·2019-08-26 12:20