你的位置:首页 > Java教程

[Java教程]AngularJS路由系列


 

本系列探寻AngularJS的路由机制,在WebStorm下开发。主要包括:

 

● 刷新路由
● 查看当前路由以及所有路由
● 路由触发事件
● 获取路由参数

● 路由的resolve属性
● 路由URL格式

 

项目文件结构

 

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>

 

 

■ 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="http://www.cnblogs.com//"></head><a href="http://www.cnblogs.com//">School Buddy</a><a href="http://www.cnblogs.com//schools">Schools</a><a href="http://www.cnblogs.com//classrooms">Classrooms</a><a href="http://www.cnblogs.com//activities">Activities</a>

 

但是url使用HTML5格式有一个不好的地方,当输入一个不存在的url就会报一个404的错。

 

 

未完待续~~