本系列探尋AngularJS的路由機制,在WebStorm下開發。主要包括:css
● 刷新路由
● 查看當前路由以及全部路由
● 路由觸發事件
● 獲取路由參數html
● 路由的resolve屬性
● 路由URL格式html5
AngularJS路由系列包括:
一、AngularJS路由系列(1)--基本路由配置
二、AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,獲取路由參數,路由的Resolve
三、AngularJS路由系列(3)-- UI-Router初體驗
四、AngularJS路由系列(4)-- UI-Router的$state服務、路由事件、獲取路由參數
五、AngularJS路由系列(5)-- UI-Router的路由約束、Resolve屬性、路由附加數據、路由進入退出事件
六、AngularJS路由系列(6)-- UI-Router的嵌套Statenode
node_modules/jquery
public/
.....app/
..........bower_components/
...............toastr/
....................toastr.min.css
....................toastr.min.js
...............jquery/
....................dist/
.........................jquery.min.js
...............angular/
....................angular.min.js
...............angular-ui-router/
....................release/
.........................angular-ui-router.min.js
...............angular-route/
.........................angular-route.min.js
..........controllers/
...............HomeController.js
...............AllSchoolsController.js
...............AllClassroomsController.js
...............AllActivityiesController.js
...............ClassroomController.js
...............ClassroomSummaryController.js
...............ClassroomMessageController.js
..........css/
...............bootstrap.cerulean.min.css
..........filters/
...............activityMonthFilter.js
..........services/
...............dataServices.js
...............notifier.js
..........templates/
...............home.html
...............allSchools.html
...............allClassrooms.html
...............allActivities.html
...............classroom.html
...............classroomDetail.html
...............classroom_parent.html
..........app.js
.....index.html
.....favicon.ico
server/
.....data/
.....routes/
.....views/
.....helpers.js
package.json
server.jsjson
有時候咱們須要刷新路由而不是整個頁面。該如何作呢?ngRoute這個module爲咱們準備了$route服務的reload()方法能夠實現。bootstrap
■ HomeController.js, 添加刷新路由功能,$route.reload數組
(function(){ angular.module('app') .controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]); function HomeController(dataService, notifier, $route, $log){ var vm = this; vm.message = 'Welcome to School Buddy!'; //從新刷新路由 vm.refresh = function(){ $route.reload(); } dataService.getAllSchools() .then(function(schools){ vm.allSchools = schools; vm.schollCount = schools.length; }) .catch(showError); dataService.getAllClassrooms() .then(function(classroom){ vm.allClassrooms = classrooms; vm.classroomCount = classrooms.length; }) .catch(showError); dataService.getAllActivities() then(function(activities)){ vm.allActivities = activities; vm.activityCount = activities.length; } ... } }());
■ home.html, 添加刷新路由的按鈕promise
{{home.message}}
{{home.schoolCount}}
{{home.activityCount}}瀏覽器
<button ng-click="home.refresh()">刷新</button>
當點擊"刷新"按鈕,刷新的只是路由,不是整個頁面。
$route服務提供了current和routes屬性。
■ HomeController.js, 查看當前路由以及全部路由,使用$route的current和routes屬性
(function(){ angular.module('app') .controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]); function HomeController(dataService, notifier, $route, $log){ var vm = this; vm.message = 'Welcome to School Buddy!'; //從新刷新路由 vm.refresh = function(){ $log.debug($route.current); $log.debut($route.routes); $route.reload(); } dataService.getAllSchools() .then(function(schools){ vm.allSchools = schools; vm.schollCount = schools.length; }) .catch(showError); dataService.getAllClassrooms() .then(function(classroom){ vm.allClassrooms = classrooms; vm.classroomCount = classrooms.length; }) .catch(showError); dataService.getAllActivities() then(function(activities)){ vm.allActivities = activities; vm.activityCount = activities.length; } ... } }());
$route.current相關的以下:
$route.routes相關以下:
$route服務提供如下幾個事件:
● $routeChangeStart
● $routeChangesSuccess
● $routeChangeError
● $routeUpdate
使用$on來調用事件
這些事件是在路由發生變化、路由成功、路由異常、路由更新時被觸發。誰來作這些事件的載體呢?咱們能夠使用$on方法把這些事件註冊到$rootScope上去,這樣,在全局範圍內觸發這些事件。
■ app.js,爲$rootScope添加路由事件
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){ $logProvider.debugEnabled(true); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html', resolve:{ promise: function(){ throw 'error transitioning to classrooms'; } } }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
以上,使用resolve在controller初始化以前定義一個promise方法故意拋出一個異常。
在localhost:3000/#/下刷新瀏覽器,即觸發了$rootScope的$routeChangeSuccess事件。
可見,event用來存放當前觸發事件;current用來存放當前路由,previous用來存放上一個路由。
清空控制檯記錄,點擊導航欄上的Activities
previous顯示了上一個路由。
清空控制檯記錄,點擊導航欄上的Classroom,即咱們故意在該路由中設置了一個異常。
有時候咱們須要獲取路由中的參數,ngRoute爲咱們提供了$routeParams服務。
.when('/classrooms/:id',{
})
在控制器中大體這樣:
function ClassroomController($routeParams){ var classroomID = $routeParams.id; //使用classroomID獲取相關Classroom }
■ app.js,針對Classroom添加一個帶參數的路由
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){ $logProvider.debugEnabled(true); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html' }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html' }) .when('/classrooms/:id',{ templateUrl: '/app/templates/classroom.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
■ ClassroomController.js
(function(){ angular.module('app',[]) .controller('ClassroomController', ['dataService','notifier', '$routeParams', ClassroomController]); function ClassroomController(dataService, notifier, $routeParams){ var vm = this; dataService.getClassroom($routeParams.id) .then(function(classroom){ vm.currentClassroom = classroom; }) .catch(showError); function showError(message){ notifier.error(message); } } }());
在瀏覽器中輸入localhost:3000/#/classrooms/1
■ app.js,針對Classroom添加帶更多參數的路由
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){ $logProvider.debugEnabled(true); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html' }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html' }) .when('/classrooms/:id',{ templateUrl: '/app/templates/classroom.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .when('/classroom/:id/detail/:month?',{ templateUrl: '/app/templates/classroomDetail.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
■ ClassroomController.js
(function(){ angular.module('app',[]) .controller('ClassroomController', ['dataService','notifier', '$routeParams', ClassroomController]); function ClassroomController(dataService, notifier, $routeParams){ var vm = this; vm.month = $routeParams.month; dataService.getClassroom($routeParams.id) .then(function(classroom){ vm.currentClassroom = classroom; //判斷路由中是否有month這個參數 if($routeParams.month){ //集合或數組是否爲空 if(classroom.activities.length > 0){ vm.timePeriod = dataService.getMonthName($routeParams.month); } else { vm.timePeriod = 'No activities this month'; } } else { vm.timePeriod = 'All activities'; } }) .catch(showError); function showError(message){ notifier.error(message); } } }());
■ AllActivitiesController.js
(function(){ angular.module('app') .controller('AllActivitiesController', ['dataService', 'notifier', '$location', AllActivitiesController]); function AllActivitiesController(dataService, notifier, $location){ var vm = this; vm.seletedMonth = 1; //搜索過濾 vm.search = function(){ var classroom_detail_url = '/classrooms/' + vm.selectedClassroom.id + '/detail/' +vm.seletedMonth; $location.url(classroom_detail_url); }; dataService.getAllClassrooms() .then(function(classrooms){ vm.allClassrooms = classroom; vm.seletedClassroom = classrooms[0]; }) .catch(showError); dataService.getAllActivities() .then(function(activities)){ vm.allActivities = activities; } .catch(showError); function showError(message){ notifier.error(message); } } }());
可見,使用$location.url(route)方法能夠輕鬆轉到任何路由。
在配置路由的時候有一個Resolve屬性,接受一個Object對象,對象的每個屬性接收一個函數,resolve中的事件發生在controller初始化以前,最終也將被注入到controller中。
.when('/activities', { controller: 'AllActivitiesController', controllerAd: 'activities', templateUrl: '/app/tempaltes/allActivities.html', resolve: { activities: function(dataService){ return dataService.getAllActivities(); } } })
以上,dataService.getAllActivities方法返回一個promise,必需要被resolved以後纔會轉到相應的視圖頁。接着,activities能夠被注入到AllActivitiesController中。
■ app.js, 在/activities路由下加上resolve屬性
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){ $logProvider.debugEnabled(true); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html' }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html', resolve: { activities: function(dataService){ return dataService.getAllActivites(); } } }) .when('/classrooms/:id',{ templateUrl: '/app/templates/classroom.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .when('/classroom/:id/detail/:month?',{ templateUrl: '/app/templates/classroomDetail.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
■ AllActivitiesController.js,把路由resolve屬性中的activities注入到控制器中來
(function(){ angular.module('app') .controller('AllActivitiesController', ['dataService', 'notifier', '$location', 'activities', AllActivitiesController]); function AllActivitiesController(dataService, notifier, $location, activities){ var vm = this; vm.seletedMonth = 1; //這裏的activites中路由的resolve中來 //原先的getAllActivities方法就不須要存在了 vm.allActivities = actvities; //搜索過濾 vm.search = function(){ var classroom_detail_url = '/classrooms/' + vm.selectedClassroom.id + '/detail/' +vm.seletedMonth; $location.url(classroom_detail_url); }; dataService.getAllClassrooms() .then(function(classrooms){ vm.allClassrooms = classroom; vm.seletedClassroom = classrooms[0]; }) .catch(showError); function showError(message){ notifier.error(message); } } }());
使用resolve的好處是:當視圖頁向$scope要數據的時候,因爲在controller實例化以前已經準備好了數據,因此頁面延遲時間大大縮短。
● Hashbang格式: localhost:3000/#/classrooms/1/detail/12,默認的就是這種格式
● HTML5格式:localhost:3000/classrooms/1/detail/12,須要使用$locationProvider配置,若是使用這種格式,但瀏覽器不支持HTML5的歷史API,Angular就會使用Hashbang格式。
■ app.js, 引用$locationProvider配置自定義url格式
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', '$locationProvider', function($logProvider,$routeProvider, $locationProvider){ $logProvider.debugEnabled(true); //使用自定義url格式 $locationProvider.hasPrefix('!'); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html' }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html', resolve: { activities: function(dataService){ return dataService.getAllActivites(); } } }) .when('/classrooms/:id',{ templateUrl: '/app/templates/classroom.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .when('/classroom/:id/detail/:month?',{ templateUrl: '/app/templates/classroomDetail.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
■ index.html,爲了配合以上的自定義url格式須要作些改變
<a href="#!/">School Buddy</a> <a href="#!/schools">Schools</a> <a href="#!/classrooms">Classrooms</a> <a href="#!/activities">Activities</a>
■ app.js, 引用$locationProvider配置HTML5的url格式
(function(){ var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', '$locationProvider', function($logProvider,$routeProvider, $locationProvider){ $logProvider.debugEnabled(true); //使用HTML5格式 $locationProvider.html5Mode({ enabled: true, requireBase: true, //須要配置基地址 rewriteLinks: true //遇到舊版本的瀏覽器會使用默認的Hashbang模式 }); $routeProvider .when('/',{ controller: 'HomeController' , controllerAs: 'home', templateUrl: '/app/templates/home.html' }) .when('/schools',{ controller: 'AllSchoolsController', controllerAs: 'schools', templateUrl: '/app/templates/allSchools.html', caseInsensitiveMatch: true }) .when('/classrooms/:id',{ controller: 'AllClassroomsController', controllerAs: 'classrooms', templateUrl: '/app/templates/allClassrooms.html' }) .when('/activities',{ controller: 'AllActivitiesController', controllerAs: 'activities', templateUrl: '/app/templates/allActivities.html', resolve: { activities: function(dataService){ return dataService.getAllActivites(); } } }) .when('/classrooms/:id',{ templateUrl: '/app/templates/classroom.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .when('/classroom/:id/detail/:month?',{ templateUrl: '/app/templates/classroomDetail.html', controller: 'ClassroomController', controllerAs: 'classroom' }) .otherwise('/'); }]); app.run(['$rootScope', '$log', function($rootScope, $log){ //經過$on爲$rootScope添加路由事件 $rootScope.$on('$routeChangeSuccess',function(event, current, previous){ $log.debug('successfully changed routes'); $log.debug(event); $log.debug(current); $log.debug(previous); }); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){ $log.debug('error changing routes'); $log.debug(event); $log.debug(current); $log.debug(previous); $log.debug(rejection); }); }]); }());
■ index.html,爲了配合URL的HTML5格式須要作些改變
<head> <base href="/"> </head> <a href="/">School Buddy</a> <a href="/schools">Schools</a> <a href="/classrooms">Classrooms</a> <a href="/activities">Activities</a>
可是url使用HTML5格式有一個很差的地方,當輸入一個不存在的url就會報一個404的錯。
未完待續~~