上一節咱們走通了基本的SPA基礎結構,這一節會更完全的將後端的視圖、路由、控制器所有移到前端。篇幅比較長,主要分頁面改造、使用AngularUI兩大部分以及一些優化路由、使用Angular的其餘指令的學習。篇幅雖然長,但熟悉了就是這個套路,特別是第一部分。重點是理解Angular這種操做數據而不是操做Dom的編程方式。javascript
1、移除服務端依賴css
上一節中咱們還保留了基於jade的layout。爲此還保留一個Express的控制器。這一節咱們所有在客戶端(app_client)實現。先在app_client目錄下建立一個index.html(等因而layout.jade生成的頁面)html
<!DOCTYPE html> <html ng-app="readApp"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="/bootstrap/css/bootstrap.css" /> <link rel="stylesheet" href="/stylesheets/style.css" /> <title>ReadingClub</title> </head> <body> <nav class="navbar navbar-default navbar-fixed-top navbar-inverse"> <div class="container"> <div class="navbar-header"><a href="/" class="navbar-brand">ReadingClub</a></div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav pull-right"> <li><a href="/">首頁</a></li> <li><a href="/books">讀物</a></li> <li><a href="/about">關於</a></li> <li><a href="/register">註冊</a></li> <li><a href="/login">登陸</a></li> </ul> </div> </div> </nav> <div id="bodycontent" class="container" > <div ng-view> </div> </div> <footer class="container"> <div class="row"> <div class="col-xs-12"><small>© stoneniqiu 2016</small></div> </div> </footer> <script src="/angular/angular.min.js"></script> <script src="/lib/angular-route.min.js"></script> <script src="/angular/readApp.min.js"></script> <!--script(src='/app.js')--> <!--script(src='/home/home.controller.js')--> <!--script(src='/common/services/ReadData.service.js')--> <!--script(src='/common/filters/formatDate.filter.js')--> <!--script(src='/common/directive/ratingStars/ratingStars.directive.js')--> <script src="/javascripts/jquery-1.11.1.min.js"></script> <script src="/plupload-2.1.8/js/plupload.full.min.js"> </script> <script src="/javascripts/books.js"></script> </body> </html>
咱們已經使用了Angular的路由,就不想還要維護Express的路由。固然這個文件仍是須要Express給咱們返回的,因而修改根目錄下的app.js前端
//app.use('/', routes); app.use('/api', routesApi); app.use(function (req, res) { res.sendfile(path.join(__dirname, 'app_client', 'index.html')); });
註釋掉app.use('/', routes),保留api部分,使用app.use,只要請求到達這裏都會返回index.html。固然Angular不會每次都請求這個頁面。這個時候運行,頁面上尚未什麼變化。index.html頁面有太多標籤,接下來將header和footer做爲指令提出來,這便於之後替換和複用。要注意的是若是你想把指令用作元素,就不能使用html元素的名稱命名。因此footer命名爲footerNav,或者別的什麼你喜歡的名字均可以。html5
先在directive目錄下建立一個footer文件夾,建立一個footer.html,把index.html中的footer部分拿過來。java
<footer class="container"> <div class="row"> <div class="col-xs-12"><small>© stoneniqiu 2016</small></div> </div> </footer>
而後在同目錄下建立一個footer.js,建立指令爲footerNavjquery
(function () { angular .module('readApp') .directive('footerNav', footerNav); function footerNav() { return { restrict: 'EA', templateUrl: '/common/directive/footer/footer.html' }; } })();
別忘記添加進appClientFiles 數組中git
var appClientFiles = [ 'app_client/app.js', 'app_client/home/home.controller.js', 'app_client/common/services/ReadData.service.js', 'app_client/common/filters/formatDate.filter.js', 'app_client/common/directive/ratingStars/ratingStars.directive.js', 'app_client/common/directive/footer/footer.js' ];
使用:程序員
<footer-nav></footer-nav>
會生成:angularjs
這樣就完成了footer部分的改造。
同理對於導航條,也是上面的幾個步驟,這裏就不贅述了。
使用的時候調用,這樣就很方便了。
<navigation></navigation>
這樣讓每一個文件只作一件事,之後須要使用某個組件能夠直接拿過去。
如今咱們能夠修改下以前定義的home.view.html,將導航和footer加過來。
<navigation></navigation> <div id="bodycontent" class="container"> <div class="row"> <div class="col-md-9 page"> <div class="row topictype"><a href="/" class="label label-info">所有</a><a href="/">讀書</a><a href="/">書評</a><a href="/">求書</a><a href="/">求索</a></div> <div class="error">{{ vm.message }}</div> <div class="row topiclist" data-ng-repeat='topic in vm.data'> <img data-ng-src='{{topic.img}}'><span class="count"><i class="coment">{{topic.commentCount}}</i><i>/</i><i>{{topic.visitedCount}}</i></span> <span class="label label-info">{{topic.type}}</span><a href="/">{{topic.title}}</a> <span class="pull-right">{{topic.createdOn | formatDate}}</span><a href="/" class="pull-right author">{{topic.author}}</a> </div> </div> <div class="col-md-3"> <div class="userinfo"> <p>{{vm.user.userName}}</p> </div> </div> </div> </div> <footer-part></footer-part>
頁面的結構完整了。增長了navigation、footer和container 。因而乎,index.html只須要保留如下內容
<!DOCTYPE html> <html ng-app="readApp"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="/bootstrap/css/bootstrap.css" /> <link rel="stylesheet" href="/stylesheets/style.css" /> <title>ReadingClub</title> <base href="/" /> </head> <body ng-view> <script src="/angular/angular.min.js"></script> <script src="/lib/angular-route.min.js"></script> <script src="/angular/readApp.min.js"></script> <!--script(src='/app.js')--> <!--script(src='/home/home.controller.js')--> <!--script(src='/common/services/ReadData.service.js')--> <!--script(src='/common/filters/formatDate.filter.js')--> <!--script(src='/common/directive/ratingStars/ratingStars.directive.js')--> <script src="/javascripts/jquery-1.11.1.min.js"></script> <script src="/plupload-2.1.8/js/plupload.full.min.js"> </script> <script src="/javascripts/books.js"></script> </body> </html>
ng-view位於body上了,到目前爲止路由、視圖都是由Angular管理了。咱們只用Express返回了須要的資源文件。
2、路由優化
在上一節使用Angular路由的時候,地址上回多出一個#號,看上去不太美觀,Angular提供了方法從地址欄移除#號,但這個功能在ie9及如下有兼容性問題,因此若是顧及到ie9及如下版本,能夠跳過這個部分。由於這裏使用了H5的一個特性。使用$locationProvider切到h5模式:
(function() { angular.module('readApp', ['ngRoute']); function config($routeProvider, $locationProvider) { $routeProvider .when('/', { templateUrl: 'home/home.view.html', controller: 'homeCtrl', controllerAs: 'vm' }) .otherwise({ redirectTo: '/' }); $locationProvider.html5Mode(true); } angular .module('readApp') .config(['$routeProvider', '$locationProvider', config]); } )();
可是若是出現瞭如下錯誤:
須要在head中作如下修改:
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="/bootstrap/css/bootstrap.css" /> <link rel="stylesheet" href="/stylesheets/style.css" /> <title>ReadingClub</title> <base href="/" /> </head>
如今瀏覽頁面,#號已經消失了。IE9中還存在。若是是兼容性視圖,頁面將是一片空白。AngularJS 1.3拋棄了對IE8的支持,AngularJS 1.2將繼續支持IE8,但核心團隊已經不打算在解決IE8及以前版本的問題上花時間。因此這一點要注意到。接下來改造更多的頁面
3、頁面改造
前四章咱們用jade模板製做了幾個頁面,目前只改造了index.html。接下來繼續Angular化。
在app_client目錄下建立一個about文件夾,並新建一個about.controller.js。包含一個user,一個title,和一個list,也就是咱們讀過的書。定義爲aboutCtrl。
(function () { angular .module('readApp') .controller('aboutCtrl', aboutCtrl); function aboutCtrl() { var vm = this; vm.title = 'ReadingClub'; vm.user = { userName: "stoneniqiu", }; vm.list = [ "第一期 《失控》 -- 上海-stoneniqiu", "第二期 《代碼整潔之道》 -- 上海-stoneniqiu", "第三期 《女人的起源》 -- 長沙-素情", "第四期 《數學之美》 -- 廣州_Watery.D.Lotus", "第五期 《卓有成效的管理者》 -- 北京-卡薩布蘭卡", "第六期 《異類》 -- 上海-stoneniqiu", "第七期 《設計心理學》 -- 北京--彥聖", "第八期 《烏合之衆》 -- 廣州_Watery.D.Lotus & 上海_stoneniqiu", "第九期 《國富論》 -- 上海-stoneniqiu", "第十期 《少有人走的路》 -- 深圳-一路風景", "第十一期 《程序員修煉之道》 -- stoneniqiu", "第十二期 《性格色彩》 -- 上海_星空" ]; } })();
並加入到appClientFiles中。
var appClientFiles = [ 'app_client/app.js', 'app_client/home/home.controller.js', 'app_client/common/services/ReadData.service.js', 'app_client/common/filters/formatDate.filter.js', 'app_client/common/directive/ratingStars/ratingStars.directive.js', 'app_client/common/directive/footer/footer.js', 'app_client/common/directive/navigation/navigation.js', 'app_client/about/about.controller.js', ];
而後再增長一個about.html:
<navigation></navigation> <div id="bodycontent" class="container"> <div class="row"> <div class="col-md-9 page"> <p >歡迎來到{{vm.title}},咱們一塊兒讀過的書: </p> <ul> <li ng-repeat='book in vm.list' > <span>{{book}}</span> </li> </ul> <img src="imgs/read.jpg"/> </div> <div class="col-md-3"> <div class="userinfo"> <p>{{vm.user.userName}}</p> </div> </div> </div> </div> <footer-part></footer-part>
而後加入路由:
function config($routeProvider, $locationProvider) { $routeProvider .when('/', { templateUrl: 'home/home.view.html', controller: 'homeCtrl', controllerAs: 'vm' }).when('/about', { templateUrl: 'about/about.html', controller: 'aboutCtrl', caseInsensitiveMatch: true, controllerAs: 'vm' }) .otherwise({ redirectTo: '/' }); $locationProvider.html5Mode(true); }
Angular路由默認是大小寫敏感的,若是要忽略掉大小寫,須要加上caseInsensitiveMatch: true。這個時候訪問頁面:
若是個人title是這樣:
vm.title = '<b>ReadingClub</b>';
頁面上會直接獲得:
歡迎來到<b>ReadingClub</b>,咱們一塊兒讀過的書:
如何不把標籤當字符輸出呢,這就要用到angular-sanitize。下載angular-sanitize的min.js和map文件, 放置在lib目錄下。 https://code.angularjs.org/1.4.6/ 並在index.html中引用:
<script src="/lib/angular-sanitize.min.js"></script>
而後還須要增長模塊依賴,和路由模塊同樣,修改app_client/app.js 模塊名稱爲ngSanitize
angular.module('readApp', ['ngRoute', 'ngSanitize']);
而後就能夠在頁面上使用ng-bind-htm來顯示html片斷。
<p >歡迎來到<span ng-bind-html="vm.title"></span>,咱們一塊兒讀過的書: </p>
這稍微顯得有點麻煩,多了一個元素,且內容不能拼接。若是是Asp.net MVC 一個@Html.Raw()就好。jade也就多個符號。
books這個頁面和index頁面很類似,稍微有點不一樣的是對應的service:
angular .module('readApp') .service('topicData', topicData) .service('booksData', booksData) .service('userData', userData); topicData.$inject = ['$http']; function topicData ($http) { return $http.get('/api/topics'); }; booksData.$inject = ['$http']; function booksData($http) { var getBooks = $http.get('/api/books'); var getbookById = function(bookid) { return $http.get('/api/book/' + bookid); }; return { getBooks: getBooks, getbookById: getbookById }; }; function userData() { return { userName: "stoneniqiu", }; }
建立一個booksData 服務,包含兩個方法,一個是getBooks,一個是getbookById。而後順便將user部分作成了userData,在後面會使用真正的用戶數據。一樣在app_client下建立一個books文件夾,新建books.controller.js和books.html
控制器:
(function () { angular .module('readApp') .controller('booksCtrl', booksCtrl); booksCtrl.$inject = ['booksData', 'userData']; function booksCtrl(booksData,user) { var vm = this; vm.message = "loading..."; booksData.getBooks.success(function (data) { vm.message = data.length > 0 ? "" : "暫無數據"; vm.books = data; }).error(function (e) { console.log(e); vm.message = "Sorry, something's gone wrong "; }); vm.user = user; } })();
習慣性囉嗦一句,記得加入appClientFiles,生成壓縮文件
視圖:
<navigation></navigation> <div id="bodycontent" class="container"> <div class="row"> <div class="col-md-9 page"> <div class="row booklist" ng-repeat="book in vm.books|orderBy:'rating':true"> <div class="col-md-2"> <img data-ng-src='{{book.img}}'></div> <div class="col-md-10"> <p> <a href="/book/{{book._id}}">{{book.title}}</a> <span class="close" data-id="{{book._id}}">×</span> </p> <p>{{book.info}}</p> <p rating-stars rating="book.rating"></p> <p class="tags"> <span ng-repeat="tag in book.tags">{{tag}}</span> </p> </div> </div> </div> <div class="col-md-3"> <div class="userinfo"> <p>{{vm.user.userName}}</p> </div> </div> </div> </div> <footer-part></footer-part>
和以往不一樣的是,使用了一個Angular自帶的filter:orderby。第一個參數是字段名,第二個參數默認是false,true是表明降序。
路由:
.when('/books', { templateUrl: 'books/books.html', controller: 'booksCtrl', caseInsensitiveMatch: true, controllerAs: 'vm' })
這個時候頁面出來了。一見如故:
還須要增長一個detail的頁面,但不想再講上面的步驟了,說白了,都是套路。強調兩個不一樣的地方。一個是路由傳遞參數
.when('/book/:bookid', { templateUrl: 'bookDetail/bookDetail.html', controller: 'bookDetailCtrl', caseInsensitiveMatch: true, controllerAs: 'vm' })
這個寫法和Express中定義路由參數同樣。二個是控制器使用$routeParams獲取參數
(function () { angular .module('readApp') .controller('bookDetailCtrl', bookDetailCtrl); bookDetailCtrl.$inject = ['$routeParams','booksData', 'userData']; function bookDetailCtrl($routeParams, booksData, user) { var vm = this; var bookid = $routeParams.bookid; booksData.getbookById(bookid).success(function(data) { vm.book = data; }).error(function (e) { console.log(e); vm.message = "Sorry, something's gone wrong "; }); vm.user = user; vm.closed = false; } })();
其餘地方不清楚的能夠參考頁尾提供的源碼。
4、Angular-ui-bootstrap
到如今新增和刪除沒有作。接下來使用Bootstrap的模態對話框來完成新增功能。惋惜http://angular-ui.github.io/bootstrap/ 官網打不開,能夠在 http://www.bootcdn.cn/angular-ui-bootstrap/ 下載。這個AngularUI已經定義了20多個組件,由於沒有使用所有的組件,只是使用了modal。因此能夠引用定製版 http://files.cnblogs.com/files/stoneniqiu/ui-bootstrap-custom.zip 接下來的部分有點複雜,各位看官請耐心...
<script src="/lib/ui-bootstrap-custom-0.12.0.min.js"></script> <script src="/lib/ui-bootstrap-custom-tpls-0.12.0.min.js"></script>
在booksCtrl上添加modal依賴:
booksCtrl.$inject = ['booksData', 'userData', '$modal']; function booksCtrl(booksData, user, $modal) {
這樣就能夠在這個控制器中使用模態對話框,而後給頁面元素增長點擊事件
添加事件使用的是ng-click,這是咱們第一次使用這個指令。後面會用來作刪除。
<div class="col-md-3"> <div class="userinfo"> <p>{{vm.user.userName}}</p> <a ng-click="vm.popupForm()" class="btn btn-info">新增推薦</a> </div> </div>
在booksCtrl中添加一個方法vm.popupForm。你能夠先試驗一下
vm.popupForm = function () { alert("添加"); };
但真正在這個地方咱們須要制定templateUrl和控制器
vm.popupForm = function () { var modalInstance = $modal.open({ templateUrl: '/bookModal/bookModal.html', controller: 'bookModalCtrl as vm', }); };
「bookModalCtrl as vm 」是controllerAs方法另一種用法,意思指定自控制器bookModalCtrl也啓用controllerAS語法。咱們還須要建立一個bookModalCtrl控制器和bookModal.html模板視圖。如今能夠想一下,這個視圖須要哪些元素,由於是增長一本新書。天然是要包含模型的一些字段,還注意到咱們建立了一個modalInstance的實例。在app_client下建立一個bookModal文件夾,再建立bookModal.html:
<div class="modal-content"> <form id="addReview" name="addReview" role="form" ng-submit="vm.onSubmit()" class="form-horizontal"> <div class="modal-header"> <button type="button" ng-click="vm.modal.cancel()" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> <h4 id="myModalLabel" class="modal-title">新增推薦</h4> </div> <div class="modal-body"> <div role="alert" ng-show="vm.formError" class="alert alert-danger">{{vm.formError}}</div> <div class="form-group"> <label for="name" class="col-xs-2 col-sm-2 control-label">書名</label> <div class="col-xs-10 col-sm-10"> <input id="name" name="name" required="required" ng-model="vm.formData.title" class="form-control" /> </div> </div> <div class="form-group"> <label for="info" class="col-xs-2 col-sm-2 control-label">信息</label> <div class="col-xs-10 col-sm-10"> <input id="info" name="info" required="required" ng-model="vm.formData.info" class="form-control" /> </div> </div> <div class="form-group"> <label for="ISBN" class="col-xs-2 col-sm-2 control-label">ISBN</label> <div class="col-xs-10 col-sm-10"> <input id="ISBN" name="ISBN" required="required" ng-model="vm.formData.ISBN" class="form-control" /> </div> </div> <div class="form-group"> <label for="tags" class="col-xs-2 col-sm-2 control-label">標籤</label> <div class="col-xs-10 col-sm-10"> <input id="tags" name="tags" required="required" ng-model="vm.formData.tags" class="form-control" /> </div> </div> <div class="form-group"> <label for="rating" class="col-xs-10 col-sm-2 control-label">推薦指數</label> <div class="col-xs-12 col-sm-2"> <select id="rating" required="required" name="rating" ng-model="vm.formData.rating" class="form-control input-sm"> <option>5</option> <option>4</option> <option>3</option> <option>2</option> <option>1</option> </select> </div> </div> <div class="form-group"> <label for="brief" class="col-sm-2 control-label">簡介</label> <div class="col-sm-10"> <textarea id="review" name="brief" rows="5" required="required" ng-model="vm.formData.brief" class="form-control"></textarea> </div> </div> </div> <div class="modal-footer"> <button ng-click="vm.modal.cancel()" type="button" class="btn btn-default">取消</button> <button type="submit" class="btn btn-primary">肯定</button> </div> </form> </div>
這個頁面元素比較多,但主要部分仍是一個form。表單提交對應的是ng-submit="vm.onSubmit()" 方法,而不像平時咱們使用action。另外模態對話框的關閉是vm.modal.cancel() 方法。這兩個方法待會咱們在控制器中實現。而ng-model="vm.formData.info" 相似這樣指令的做用就是在form提交的時候會建立一個對象包含這些字段。接下來看控制器:
在bookModal目錄下建立bookModal.controller.js ,定義bookModalCtrl:
(function () { angular .module('readApp') .controller('bookModalCtrl', bookModalCtrl); bookModalCtrl.$inject = ['$modalInstance', 'booksData']; function bookModalCtrl($modalInstance, booksData) { var vm = this; vm.onSubmit = function () { console.log(vm.formData); return false; };
vm.modal = { close : function (result) { $modalInstance.close(result); }, cancel : function () { $modalInstance.dismiss('cancel'); } }; } })();
記得加入appClientFiles中。咱們注入了前面建立的modalInstance實例,close和cancel方法調用了其自身方法,而onsubmit先暫時沒有提交,能夠看一下傳輸過來的數據。點擊按鈕效果以下:
若是提交數據,在console裏面能夠看到:
這說明控制器中已經獲取到表單中的數據了。有一個問題是,若是我想給這個模態對話框傳遞參數,該怎麼作,這要用到resolve。修改booksCtrl,咱們把title內容傳過去,注意要使用return語法。
var modalInstance = $modal.open({ templateUrl: '/bookModal/bookModal.html', controller: 'bookModalCtrl as vm', resolve : { viewData: function () { return { title: "新增推薦", }; } } });
在這裏建立了一個viewData對象,在模態頁面調用以下。
<h4 id="myModalLabel" class="modal-title">{{ vm.viewData.title }}</h4>
接下來就是如何把數據提交到控制器呢? 首先咱們須要定義Service,由於尚未添加book的方法,而後能夠想到的是,須要驗證數據後,而後提交到api,而後再更新視圖。
修改booksData,增長兩個方法,一個post方式增長,一個delete方法刪除。 這些api都是第三節的時候建立的。
booksData.$inject = ['$http']; function booksData($http) { var getBooks = $http.get('/api/books'); var getbookById = function(bookid) { return $http.get('/api/book/' + bookid); }; var addBook = function(data) { return $http.post("/api/book", data); }; var removeBookById = function(bookid) { return $http.delete('/api/book/' + bookid); }; return { getBooks: getBooks, getbookById: getbookById, addBook: addBook, removeBookById: removeBookById }; };
(function () { angular .module('readApp') .controller('bookModalCtrl', bookModalCtrl); bookModalCtrl.$inject = ['$modalInstance', 'viewData','booksData']; function bookModalCtrl($modalInstance, viewData, booksData) { var vm = this; vm.viewData = viewData; vm.onSubmit = function () { vm.formError = ""; if (!vm.formData.title || !vm.formData.rating || !vm.formData.brief || !vm.formData.info || !vm.formData.ISBN) { vm.formError = "請完成全部欄目!"; return false; } else { console.log(vm.formData); vm.doAddBook(vm.formData);
return false; } }; vm.doAddBook = function (formData) { booksData.addBook({ title: formData.title, info: formData.info, ISBN: formData.ISBN, brief: formData.brief, tags: formData.tags, img: formData.img, rating: formData.rating, }).success(function(data) { console.log("success!"); vm.modal.close(data); }).error(function(data) { vm.formError = "添加失敗,請再試一次"; }); return false; }; vm.modal = { close : function (result) { $modalInstance.close(result); }, cancel : function () { $modalInstance.dismiss('cancel'); } }; } })();
以上只是簡單的驗證,只是判斷是否爲空,若是有爲空的就返回。並賦值vm.formError。
<div role="alert" ng-show="vm.formError" class="alert alert-danger">{{vm.formError}}</div>
咱們在頁面上使用了ng-show, ng-show後面的表達式爲true的時候,內容就會顯示。也就是說字段不爲空,就會提示出來。
若是數據都不爲空,咱們就提交api。成功以後,記得關閉對話框。也就是在success中調用了modal.close 。可是如何更新視圖呢?modal的close方法會返回一個promise到父級控制器。所以能夠這樣處理。
booksCtrl:
vm.popupForm = function () { var modalInstance = $modal.open({ templateUrl: '/bookModal/bookModal.html', controller: 'bookModalCtrl as vm', resolve : { viewData: function () { return { title: "新增推薦", }; } } }); modalInstance.result.then(function (data) { vm.books.push(data); }); };
這個時候添加完數據,頁面上面當即更新了。不像之前操做dom的方式,咱們須要手動拼湊html。如今只須要更新模型了。
如今還差一個刪除方法,前面咱們已經使用了ng-click指令,一樣,咱們修改books.html這個視圖
<p> <a href="/book/{{book._id}}">{{book.title}}</a> <span class="close" ng-click="vm.removeBook(book._id)">×</span> </p>
定義了一個removeBook的方法,接下來在後臺實現(booksCtrl):
vm.removeBook = function (id) { if (confirm("肯定刪除?")) { booksData.removeBookById(id).success(function () { for (var i = 0; i < vm.books.length; i++) { if (vm.books[i]._id == id) { vm.books.splice(vm.books.indexOf(vm.books[i]), 1); } } }); } };
調用removeBookById方法刪除數據,成功以後再在視圖模型的中用splice方法刪除這個對象。下面看一下連貫起來的效果:
源碼:https://github.com/stoneniqiu/ReadingClub 注意分支AngularSPA下
小結:這一節篇幅比較長,但Angular構建SPA的套路已經摸清,只是頁面交互方面還不是那麼熟悉,特別是這個modal組件的使用可能讓你以爲複雜,由於從一個控制器中還調用了另一個控制器,且對這種Angular-Bootstrap組件還不熟悉。數據的驗證也顯得有點弱。可是從第五節到這兒,應該是對Angular有些感受了:和jquery直接操做demo的不一樣,它是操做視圖模型,頁面上全部變化的部分均可以經過模型來實現。另外細心的朋友可能發現了,上傳圖片的部分尚未講,限於篇幅,這一篇就先到這,後面咱們講Angular下上傳圖片,另外還有一個很重要的部分,用戶認證以及會話,盡請期待。