1. 程式人生 > >AngularJS路由系列(2)--重新整理、檢視路由,路由事件和URL格式,獲取路由引數,路由的Resolve

AngularJS路由系列(2)--重新整理、檢視路由,路由事件和URL格式,獲取路由引數,路由的Resolve

本系列探尋AngularJS的路由機制,在WebStorm下開發。主要包括:

專案檔案結構

node_modules/

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.js

重新整理路由

有時候我們需要重新整理路由而不是整個頁面。該如何做呢?ngRoute這個module為我們準備了$route服務的reload()方法可以實現。

■ 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, 新增重新整理路由的按鈕


{{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屬性

在配置路由的時候有一個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例項化之前已經準備好了資料,所以頁面延遲時間大大縮短。

路由URL格式

● 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>