本系列探寻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()方法可以实现。

5bfb10675143bf824dc7f2fbfab41398.png

■ HomeController.js, 添加刷新路由功能,$route.reload

(function(){

angular.module('app')

.controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]);functionHomeController(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}}

刷新

当点击"刷新"按钮,刷新的只是路由,不是整个页面。

d92c0dfe9ce8111c95dbd4cc3b25ca2e.png

查看当前路由以及所有路由

$route服务提供了current和routes属性。

■ HomeController.js, 查看当前路由以及所有路由,使用$route的current和routes属性

(function(){

angular.module('app')

.controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]);functionHomeController(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相关的如下:

fad9d7eb47e2c3a3698c09ea80b88aa5.png

$route.routes相关如下:

7f2b35e4cf61fec3282997c458912aed.png

路由触发事件

$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事件。

216756afe19b7952b02b38754ce07ffd.png

可见,event用来存放当前触发事件;current用来存放当前路由,previous用来存放上一个路由。

清空控制台记录,点击导航栏上的Activities

39661f6fe1e78339863b54816beadad0.png

previous显示了上一个路由。

清空控制台记录,点击导航栏上的Classroom,即我们故意在该路由中设置了一个异常。

c448d5fa68205f6ee4553a8c1ae066b0.png

获取路由参数

有时候我们需要获取路由中的参数,ngRoute为我们提供了$routeParams服务。

105d21fc83a7a2775480bdea9af6fb74.png

.when('/classrooms/:id',{

})

在控制器中大致这样:

functionClassroomController($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]);functionClassroomController(dataService, notifier, $routeParams){var vm = this;

dataService.getClassroom($routeParams.id)

.then(function(classroom){

vm.currentClassroom=classroom;

})

.catch(showError);functionshowError(message){

notifier.error(message);

}

}

}());

在浏览器中输入localhost:3000/#/classrooms/1

89dc07bf635ec87434dfa04d11d6b634.png

■ 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]);functionClassroomController(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);functionshowError(message){

notifier.error(message);

}

}

}());

■ AllActivitiesController.js

(function(){

angular.module('app')

.controller('AllActivitiesController', ['dataService', 'notifier', '$location', AllActivitiesController]);functionAllActivitiesController(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);functionshowError(message){

notifier.error(message);

}

}

}());

cd1ed62e63f643462e5a7676c47a918a.png

可见,使用$location.url(route)方法可以轻松转到任何路由。

路由的Resolve属性

在配置路由的时候有一个Resolve属性,接受一个Object对象,对象的每一个属性接收一个函数,resolve中的事件发生在controller初始化之前,最终也将被注入到controller中。

.when('/activities', {

controller:'AllActivitiesController',

controllerAd:'activities',

templateUrl:'/app/tempaltes/allActivities.html',

resolve: {

activities:function(dataService){returndataService.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){returndataService.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]);functionAllActivitiesController(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);functionshowError(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格式。

9c8ef8b269f1e043961a62900fb714b2.png

■ 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){returndataService.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格式需要做些改变

School Buddy

Schools

Classrooms

Activities

026ebc4b8f8ea6f49fc10a6f8bd0c9b0.png

■ 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){returndataService.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格式需要做些改变

School Buddy

Schools

Classrooms

Activities

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

f2610b23ab67fbe5c6a0d43f0fd35eb6.png

未完待续~~

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐