摘要:我們將使用方法創(chuàng)建一個。我們傳遞一個布爾類型,這個就是我們早先討論的的參數(shù)。再使用和構(gòu)建博客教程系列的第三部分見。所有的源碼都在上,但是應(yīng)用程序的源碼還沒有放上去,因為我們還沒有完成它,等到第三部分寫完以后再放全部的源碼到上。
注:該文作者是 John Kevin M. Basco,原文地址是 Building a blog using Flask and AngularJS Part 2
注:翻譯的第一部分請移步到 - 使用 Flask 和 AngularJS 構(gòu)建博客 - 1
這是這個教程系列的第二部分,如果你還沒有都第一部分,請移步到這里:http://blog.john.mayonvolcanosoftware.com/building-a-blog-using-flask-and-angularjs-part-1/
因為我們在該系列的第一部分已經(jīng)構(gòu)建好了 REST API ,在這部分我們將專注于構(gòu)建一個 AngularJS 應(yīng)用,用來使用我們構(gòu)建的 REST API。
目錄結(jié)構(gòu)AngularJS 應(yīng)用的目錄結(jié)構(gòu)看起來像這樣:
安裝必要的包我們將使用的包如下:
angular-route
bootstrap
restangular
angularjs
angular-local-storage
如果你不熟悉 Restangular 的話,我建議你首先讀下 Restangular 的文檔 - https://github.com/mgonto/restangular
或者如果你懶得讀它的文檔,隨時繼續(xù)閱讀本教程,當(dāng)你遇到如下對你沒有意義的 Restangular 代碼的時候,你可以參考下 Restangular 文檔。
為了使得事情更容易,在 blog/client/ 目錄創(chuàng)建一個 bower.json 文件,并且拷貝和粘貼以下內(nèi)容:
{ "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 來安裝必要的包。
初始化應(yīng)用設(shè)置首先,在 blog/client 目錄下創(chuàng)建一個新文件,并命名為 index.html,然后拷貝和粘貼以下內(nèi)容:
Blog
在以上代碼中,我們只是簡單的包含了我們稍后將要創(chuàng)建的 css 和 javascript 文件。我們也為 navigation bar, footer 等等添加了 html markup。你將注意到在以上的 gist 中,它包含了 這個 html 元素。這是一個當(dāng)前的路由將用來插入模板的地方。你將看到這些事情:ng-class="{active: isActive("/")}" 和 ng-show="isLoggedIn",忽略它們現(xiàn)在。我們將在 為 ApplicationCtrl 寫代碼的時候討論它們。
添加一些樣式現(xiàn)在讓我們添加一些樣式以便這個 app 看起來更好。為了使我們的工作更輕松,讓我們使用一個 Bootstrap 提供的主題 - http://getbootstrap.com/examples/blog/。在 app/client/css 目錄下創(chuàng)建一個新文件,并命名為 theme.css,然后拷貝和粘貼以下內(nèi)容:
/* * 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 目錄下創(chuàng)建另外一個文件,命名為 styles.css ,然后拷貝和粘貼一下內(nèi)容:
.main-view { margin-top: 20px; margin-bottom: 20px; }
現(xiàn)在進入 blog/client 目錄,然后運行這個命令:python -m SimpleHTTPServer 8000,現(xiàn)在訪問 http://localhost:8000/,你應(yīng)該可以看到應(yīng)用已經(jīng)有設(shè)計樣式。
創(chuàng)建必須的 javascript 文件注意:以下應(yīng)用使用的配置變量的值,為了假話,被硬編碼了。在生產(chǎn)環(huán)境的應(yīng)用中,需要把它放入一個配置文件中,因此你可以根據(jù)不同的環(huán)境使用不同的值,比如開發(fā)環(huán)境和生產(chǎn)環(huán)境。
現(xiàn)在讓我們創(chuàng)建必須的 javascript 文件,在 blog/client/js 目錄下創(chuàng)建一個新文件,命名為 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") } }); })
在以上代碼中,我們初始化了我們的應(yīng)用,配置 Restangular 來包括每個請求的登錄用戶的用戶名和密碼,配置 Restangular 來使用一個錯誤攔截器,它將刪除本地存儲的所有已經(jīng)保存的數(shù)據(jù),并且如果 REST API 返回一個 401 狀態(tài)碼,則把用戶跳轉(zhuǎn)到登錄頁面。
在配置部分,我們設(shè)置了 Restangular 將使用的基準(zhǔn) url,我們也設(shè)置了路由。你將注意到 /posts/create 路由有一個解決屬性包含了 redirectIfNotAuthenticated promise。是的,像你猜的那樣,如果用戶未被授權(quán),它將把用戶跳轉(zhuǎn)到登錄頁面。/sessions/create 路由也有一個解決屬性,但是它包含了 redirectIfAuthenticated promise 而不是 redirectIfNotAuthenticated promise。如果用戶被授權(quán)了,它將簡單的跳轉(zhuǎn)用戶到創(chuàng)建博客的頁面。你可以閱讀關(guān)于這個主題的更多信息 - http://blog.john.mayonvolcanosoftware.com/protecting-routes-in-angularjs/。
Factories讓我們創(chuàng)建我們的應(yīng)用將使用到的工廠方法。
Post讓我們在 blog/client/js/factories 目錄下創(chuàng)建一個新文件,命名為 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 將創(chuàng)建一個對象,該對象有一個 get 和 create 方法。我們將使用 get 方法來獲取博客列表,create 方法來創(chuàng)建一篇博客。
Session讓我們在 blog/client/js/factories 目錄下創(chuàng)建一個新文件,命名為 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 方法來授權(quán)一個用戶的 email 和密碼。你將注意到 create 方法有一個參數(shù)叫做 bypassErrorInterceptor。這個參數(shù)應(yīng)該是一個布爾類型的值(true or false),當(dāng) bypassErrorInterceptor 是 true 的時候,它將繞過我們早先在配置塊中定義的 error 攔截器。這不會立即顯得有意義,但是在后面我們將遇到一個場景,在那我們需要繞過 error 攔截器。
User在 blog/client/js/factories 目錄下創(chuàng)建一個新文件,并命名為 User.js,然后把以下代碼拷貝和粘貼進文件:
Blog.factory("User", function(Restangular) { var User; User = { create: function(user) { return Restangular .one("users") .customPOST(user); } }; return User; }
User 工廠將有一個 create 方法返回該對象。我們將使用 create 方法創(chuàng)建一個 user。
Services我們的應(yīng)用需要一個服務(wù),該服務(wù)包含登錄,登出,和校驗該用戶是否被授權(quán)的業(yè)務(wù)邏輯。我們把該服務(wù)命名為 AuthService。在 blog/cient/js/services 目錄下創(chuàng)建一個新文件,并命名為 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 認證用戶的認證信息。當(dāng)?shù)卿浾J證信息是有效的,我們將保存 base64 編碼格式的 email 和密碼到本地存儲稍后使用,并授權(quán)。當(dāng)?shù)卿浾J證信息是無效的(當(dāng)?shù)卿浾J證信息是無效的,REST API 將返回一個 401 狀態(tài)碼),我們簡單的拒絕授權(quán),并且讓使用該方法的代碼處理它??匆幌挛覀儌鬟f給 Session.create 的第二個參數(shù)。我們傳遞一個布爾類型(true),這個就是我們早先討論的 bypassErrorInterceptor 的參數(shù)。我們需要繞過在配置塊中的 error 攔截器,因為如果我們不這樣做,處理 401 錯誤碼的 handler 將把用戶跳轉(zhuǎn)到登錄視圖。我們想這個發(fā)生在其他場景,但當(dāng)我們正在登錄視圖并且用戶提交了錯誤的認證信息的時候,我們想展示一個錯誤消息,比如“Incorrect login credentials. Please try again.”。這就是為什么我們需要繞過默認的 error 攔截器,使用一個新的,包含了不同的業(yè)務(wù)邏輯(檢查以上代碼的 10 - 15 行)。
logout 方法將清理保存在本地存儲的數(shù)據(jù),isAuthenticated 方法僅僅通過檢查 token 是否被呈現(xiàn)來檢查當(dāng)前用戶是否被授權(quán)。setToken 方法保存一個 token (base64 編碼格式的 email 和 password)到本地存儲中,getToken 方法獲取保存在本地存儲中的 token。
Directives我們應(yīng)用的 registration form 將有一個 password 和 一個確認 password 字段,因此我們需要一個方法來校驗值是否匹配。為這個我們將使用 directive,在 blog/client/js/directives 目錄下創(chuàng)建一個新文件,并命名為 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 的作者。我是從別處抓取來了以上源碼,但是我沒有忘記鏈接到該站點和誰是作者。
我們現(xiàn)在完成了我們 AngularJS 應(yīng)用程序的一半。我們還沒有寫的剩下部分是控制層和模板。因為這篇文章太長了,我決定在第三部分寫控制層和模板部分。同時,研究目前為止我們已經(jīng)編寫的代碼,讓我們更熟悉它。再使用 Flask 和 AngularJS 構(gòu)建博客教程系列的第三部分見。
所有的 REST API 源碼都在 Github 上 - https://github.com/basco-johnkevin/building-a-blog-using-flask-and-angularjs ,但是 AngularJS 應(yīng)用程序的源碼還沒有放上去,因為我們還沒有完成它,等到第三部分寫完以后再放全部的源碼到 Github 上。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/45297.html
摘要:注原文作者,原文地址為在這個教程中,我們將使用和構(gòu)建一個博客。在開發(fā)期間,這將允許我們把它們運行在不同的端口例如和。現(xiàn)在我們將進入目錄并使用運行這個腳本。示例創(chuàng)建一篇文章為了創(chuàng)建一篇文章,你需要發(fā)送一個請求給。 注:原文作者 John Kevin M. Basco,原文地址為 Building a blog using Flask and AngularJS Part 1 在...
摘要:注原文作者,原文地址為在這個教程中,我們將使用和構(gòu)建一個博客。在開發(fā)期間,這將允許我們把它們運行在不同的端口例如和?,F(xiàn)在我們將進入目錄并使用運行這個腳本。示例創(chuàng)建一篇文章為了創(chuàng)建一篇文章,你需要發(fā)送一個請求給。 注:原文作者 John Kevin M. Basco,原文地址為 Building a blog using Flask and AngularJS Part 1 在...
摘要:推薦閱讀資源庫工具應(yīng)用程序精選列表中文版有哪些鮮為人知,但是很有意思的網(wǎng)站一份攻城獅筆記每天搜集上優(yōu)秀的項目一些有趣的民間故事超好用的谷歌瀏覽器油猴插件合集目錄資源文檔文章圖書會談教程更多庫工具管理數(shù)據(jù)部署桌面發(fā)展監(jiān)控應(yīng)用資源文檔介紹文檔教 推薦閱讀 MongoDB 資源、庫、工具、應(yīng)用程序精選列表中文版 有哪些鮮為人知,但是很有意思的網(wǎng)站? 一份攻城獅筆記 每天搜集 Github ...
摘要:下一篇譯精通使用開發(fā)二原版書名第一章之道這一章主要是介紹,包括這個框架以及它背后的項目。幸運的是,擁有一個活躍的,支持度高的社區(qū)。另外,社區(qū)還為已經(jīng)存在的工具箱里貢獻了許多有意思的工具。 下一篇:【譯】《精通使用AngularJS開發(fā)Web App》(二) 原版書名:Mastering Web Application Development with AngularJS Ch...
摘要:在被收購之后,維護并繼續(xù)發(fā)展。設(shè)置是告訴應(yīng)用在目錄尋找應(yīng)用模板。設(shè)置告訴應(yīng)用使用目錄里面的類似圖像文件等靜態(tài)文件。我們會在應(yīng)用開發(fā)過程中,保持著調(diào)試器在后臺運行。這能提供高效的開發(fā)環(huán)境。我們會把回應(yīng)狀態(tài)設(shè)為已創(chuàng)建。 編者注:我們發(fā)現(xiàn)了有趣的系列文章《30天學(xué)習(xí)30種新技術(shù)》,正在翻譯,一天一篇更新,年終禮包。下面是第23天的內(nèi)容。 今天的《30天學(xué)習(xí)30種新技術(shù)》,我決定暫時放下...
閱讀 890·2021-10-25 09:44
閱讀 1262·2021-09-23 11:56
閱讀 1183·2021-09-10 10:50
閱讀 3131·2019-08-30 15:53
閱讀 2134·2019-08-30 13:17
閱讀 617·2019-08-29 18:43
閱讀 2491·2019-08-29 12:57
閱讀 855·2019-08-26 12:20