首先是angular-ui-router的基本用法。■ 如何引用依賴angular-ui-router angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateNa
首先是angular-ui-router的基本用法。
■ 如何引用依賴angular-ui-router
angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateName, stateCofig); })
■ $stateProvider.state(stateName, stateConfig)
stateName是string類型
stateConfig是object類型
//statConfig可以為空對象
$stateProvider.state("home",{});
//state可以有子父級
$stateProvider.state("home",{});
$stateProvider.state("home.child",{})
//state可以是鏈式的
$stateProvider.state("home",{}).state("about",{}).state("photos",{});
stateConfig包含的欄位:template, templateUrl, templateProvider, controller, controllerProvider, resolve, url, params, views, abstract, onEnter, onExit, reloadOnSearch, data
■ $urlRouteProvider
$urlRouteProvider.when(whenPath, toPath)
$urlRouterProvider.otherwise(path)
$urlRouteProvider.rule(handler)
■ $state.go
$state.go(to, [,toParams],[,options])
形參to是string類型,必須,使用"^"或"."表示相對路徑;
形參toParams可空,類型是對象;
形參options可空,類型是對象,欄位包括:location為bool類型預設true,inherit為bool類型預設true, relative為對象預設$state.$current,notify為bool類型預設為true, reload為bool類型預設為false
$state.go('photos.detail')
$state.go('^')到上一級,比如從photo.detail到photo
$state.go('^.list')到相鄰state,比如從photo.detail到photo.list
$state.go('^.detail.comment')到孫子級state,比如從photo.detail到photo.detial.comment
■ ui-sref
ui-sref='stateName'
ui-sref='stateName({param:value, param:value})'
■ ui-view
==沒有名稱的ui-view
<div ui-view></div> $stateProvider.state("home",{ template: "<h1>hi</h1>" })
或者這樣配置:
$stateProvider.state("home"{ views: { "": { template: "<h1>hi</h1>" } } })
==有名稱的ui-view
<div ui-view="main"></div> $stateProvider.state("home",{ views: { "main" : { template: "<h1>hi</h1>" } } })
==多個ui-view
<div ui-view></div> <div ui-view="data"></div> $stateProvider.state("home",{ views: { "":{template: "<h1>hi</h1>"}, "data": {template: "<div>data</div>"} } })
■ 項目文件結構
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
app.js
index.html
■ 創建state和view
app.js
var photoGallery = angular.module('photoGallery',["ui.router"]); photoGallery.config(function($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise('/home'); $stateProvider .state('home',{ url: '/home', templateUrl: 'partials/home.html' }) .state('photos',{ url: '/photos', templateUrl: 'partials/photos.html' }) .state('about',{ url: '/about', templateUrl: 'partials/about.html' }) })
index.html
<!DOCTYPE html> <html lang="en" ng-app="photoGallery"> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"/> </head> <body> <h1>Welcome</h1> <div ui-view></div> <script src="node_modules/jquery/dist/jquery.js"></script> <script src="node_modules/angular/angular.js"></script> <script src="node_modules/angular-ui-router/release/angular-ui-router.js"></script> <script src="node_modules/angular-animate/angular-animate.js"></script> <script src="node_modules/bootstrap/dist/js/bootstrap.js"></script> <script src="node_modules/angular-bootstrap/ui-bootstrap-tpls.js"></script> <script src="app.js"></script> </body> </html>
■ state之間的跳轉
index.html
<nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a ui-sref="home" class="navbar-brand">Home</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li> <a ui-sref="photos">Photos</a> </li> <li> <a ui-sref="about">About</a> </li> </ul> </div> </div> </nav> <div ui-view></div>
以上通過ui-sref屬性完成state之間的跳轉。
■ 多個view以及state嵌套
有時候,一個頁面上可能有多個ui-view,比如:
<div ui-view="header"></div>
<div ui-view="body"></div>
假設,以上頁面屬於一個名稱為parent的state中。
我們知道在ui-router中,一個state大致是這樣設置的:
.state('content.photos',{ url: 'photos', views:{ "body@content":{templateUrl: 'partials/photos.html'} } })
所有state下views下的所有鍵值對(類似 "body@content":{templateUrl: 'partials/photos.html'})都被放到一個鍵值集合中。而ui-view的工作原理就是根據自己的屬性值,到這個鍵值集合中去找匹配的鍵,找到就把對應的頁面顯示出來。
點擊header對應的頁面鏈接,可能會跳轉到另外的子頁面出現在<div ui-view="body"></div>這個位置。這時候頁面出現了子父關係,而每個頁面都屬於某個state,這樣state間就出現了子父關係。這些跳轉的子頁面,在路由設置中,可能被稱為parent.son1, parent.son2...這就是state的嵌套。
在現有的文件結構上增加content.html, header.html,文件結構變為:
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
app.js
index.html
content.html 包含了多各ui-view, 一個ui-view和頁頭相關,保持不變;令一個ui-view和會根據頁頭上的點擊呈現不同的內容
<div ui-view="header"></div>
<div ui-view="body"></div>
header.html 把原先indext.html中nav部分放到這裡來
<nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a ui-sref="content.home" class="navbar-brand">Home</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li> <a ui-sref="content.photos">Photos</a> </li> <li> <a ui-sref="content.about">About</a> </li> </ul> </div> </div> </nav>
index.html 這時變成了這樣
<div ui-view></div>
app.js 路由現在這樣設置
var photoGallery = angular.module('photoGallery',["ui.router"]); photoGallery.config(function($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise('home'); $stateProvider .state('content',{ url: '/', views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } }) .state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } }) .state('content.photos',{ url: 'photos', views:{ "body@content":{templateUrl: 'partials/photos.html'} } }) .state('content.about',{ url:'about', views:{ "body@content":{templateUrl: 'partials/about.html'} } }) })
這時候,頁面是這樣呈現出來的:
→ 來到home這個路由
.state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } })
以上,告訴我們partials/home.html將會被載入到與"body@content"匹配的ui-view中。暫時對應的ui-view還沒有出現,於是等待。
→ 路由看到index.html上的<div ui-view></div>
.state('content',{ url: '/', views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } })
於是,就找到了content這個state下views下的 "":{templateUrl: 'partials/content.html'}這個鍵值對,把partials/content.html顯示出來。
→ 分別載入partials/content.html頁面上的各個部分
看到<div ui-view="header"></div>,就載入如下:
"header@content":{templateUrl: 'partials/header.html'},
看到<div ui-view="body"></div>,先載入 "body@content":{templateUrl: 'partials/home.html'}
→ 點擊header上的鏈接
點擊<a ui-sref="content.photos">Photos</a>,來到:
.state('content.photos',{ url: 'photos', views:{ "body@content":{templateUrl: 'partials/photos.html'} } })
把partials/photos.html顯示到<div ui-view="body"></div>中去。
點擊<div ui-view="body"></div>,來到:
.state('content.about',{ url:'about', views:{ "body@content":{templateUrl: 'partials/about.html'} } })
把partials/about.html顯示到<div ui-view="body"></div>中去。
■ state多級嵌套
以上,在路由設置中,state名稱有content, content.photos有了這樣的一層嵌套。接下來,要實現state的多級嵌套。
在photos.html頁面準備載入一個子頁面,叫做photos-list.html;
與photo-list.html頁面相鄰的還有一個頁面,叫做photo-detail.html;
在photo-detail.html頁面上載入一個子頁面,叫做photos-detail-comment.html;
這樣,頁面有了嵌套關係,state也相應的會有嵌套關係。
現在,文件結構變成:
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
.....photos-list.html
.....photo-detail.html
.....photos-detail-comment.html
app.js
index.html
photos.html 加一個容納子頁面的ui-view
photos
<div ui-view></div>
如何到達這個子頁面呢?修改header中的相關部分如下:
<nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a ui-sref="content.home" class="navbar-brand">Home</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li> <a ui-sref="content.photos.list">Photos</a> </li> <li> <a ui-sref="content.about">About</a> </li> </ul> </div> </div>
以上,通過<a ui-sref="content.photos.list">Photos</a>來到photos.html的子頁面photos-list.html.
photos-list.html 通過2種途徑到相鄰頁photo-detail.html
<h1>photos-list</h1> <ul> <li><a ui-sref="^.detail">我通過相對路徑到相鄰的state</a></li> <li><a ui-sref="content.photos.detail">我通過絕對路徑到相鄰的state</a></li> </ul>
photo-detail.html 又提供了來到其子頁面photos-detail-comment.html的ui-view
<h1>photo-details</h1> <a class="btn btn-default" ui-sref=".comment">通過相對路徑去子state</a> <div ui-view></div>
photos-detail-comment.html 則很簡單:
<h1>photos-detail-comment</h1>
app.js state多級嵌套的設置為
var photoGallery = angular.module('photoGallery',["ui.router"]); photoGallery.config(function($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise('home'); $stateProvider .state('content',{ url: '/', views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } }) .state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } }) .state('content.photos',{ url: 'photos', views:{ "body@content":{templateUrl: 'partials/photos.html'} } }) .state('content.photos.list',{ url: '/list', templateUrl: 'partials/photos-list.html' }) .state('content.photos.detail',{ url: '/detail', templateUrl: 'partials/photos-detail.html' }) .state('content.photos.detail.comment',{ url: '/comment', templateUrl: 'partials/photos-detail-comment.html' }) .state('content.about',{ url:'about', views:{ "body@content":{templateUrl: 'partials/about.html'} } }) })
■ 抽象state
如果一個state,沒有通過鏈接找到它,那就可以把這個state設置為abstract:true,我們把以上的content和content.photos這2個state設置為抽象。
.state('content',{ url: '/', abstract: true, views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } }) ... .state('content.photos',{ url: 'photos', abstract: true, views:{ "body@content":{templateUrl: 'partials/photos.html'} } })
那麼,當一個state設置為抽象,如果通過ui-sref或路由導航到該state會出現什麼結果呢?
--會導航到預設路由上
$urlRouterProvider.otherwise('home');
即
.state('content.home',{ url: 'home', views:{ "body@content":{templateUrl: 'partials/home.html'} } })
最終把partials/home.html顯示出來。
■ 使用控制器
在實際項目中,數據大多從controller中來。
首先在路由中設置state所用到的控制器以及控制器別名。
var photoGallery = angular.module('photoGallery',["ui.router"]); photoGallery.config(function($stateProvider, $urlRouterProvider){ $urlRouterProvider.otherwise('home'); $stateProvider .state('content',{ url: '/', abstract: true, views:{ "":{templateUrl: 'partials/content.html'}, "header@content":{templateUrl: 'partials/header.html'}, } }) .state('content.home',{ url: 'home', views:{ "body@content":{ templateUrl: 'partials/home.html', controller: 'HomeController', controllerAs: 'ctrHome' } } }) .state('content.photos',{ url: 'photos', abstract: true, views:{ "body@content":{ templateUrl: 'partials/photos.html', controller: 'PhotoController', controllerAs: 'ctrPhoto' } } }) .state('content.photos.list',{ url: '/list', templateUrl: 'partials/photos-list.html', controller: "PhotoListController", controllerAs: 'ctrPhotoList' }) .state('content.photos.detail',{ url: '/detail', templateUrl: 'partials/photos-detail.html', controller: 'PhotoDetailController', controllerAs: 'ctrPhotoDetail' }) .state('content.photos.detail.comment',{ url: '/comment', templateUrl: 'partials/photos-detail-comment.html' }) .state('content.about',{ url:'about', views:{ "body@content":{templateUrl: 'partials/about.html'} } }) })
添加controller.js,該文件用來定義所用到的controller.現在的文件結構為:
asserts/
.....css/
.....images/
..........image1.jpg
..........image2.jpg
..........image3.jpg
..........image4.jpg
node_modules/
partials/
.....about.html
.....home.html
.....photos.html
.....content.html
.....header.html
.....photos-list.html
.....photo-detail.html
.....photos-detail-comment.html
app.js
index.html
controllers.js
photoGallery.controller('HomeController',['$scope', '$state', function($scope, $state){ this.message = 'Welcome to the Photo Gallery'; }]); //別名:ctrPhoto photoGallery.controller('PhotoController',['$scope','$state', function($scope, $state){ this.photos = [ { id: 0, title: 'Photo 1', description: 'description for photo 1', imageName: 'image1.jpg', comments:[ {name: 'user1', comment: 'Nice'}, { name:'User2', comment:'Very good'} ]}, { id: 1, title: 'Photo 2', description: 'description for photo 2', imageName: 'image2.jpg', comments:[ { name: 'user2', comment: 'Nice'}, { name:'User1', comment:'Very good'} ]}, { id: 2, title: 'Photo 3', description: 'description for photo 3', imageName: 'image3.jpg', comments:[ {name: 'user1', comment: 'Nice'} ]}, { id: 3, title: 'Photo 4', description: 'description for photo 4', imageName: 'image4.jpg', comments:[ {name: 'user1', comment: 'Nice'}, { name:'User2', comment:'Very good'}, { name:'User3', comment:'So so'} ]} ]; //給子state下controller中的photos賦值 this.pullData = function(){ $scope.$$childTail.ctrPhotoList.photos = this.photos; } }]); //別名:ctrPhotoList photoGallery.controller('PhotoListController',['$scope','$state', function($scope, $state){ this.reading = false; this.photos = new Array(); this.init = function(){ this.reading = true; setTimeout(function(){ $scope.$apply(function(){ $scope.ctrPhotoList.getData(); }); }, 1500); } this.getData = function(){ //調用父state中controller中的方法 $scope.$parent.ctrPhoto.pullData(); /*this.photos = $scope.$parent.ctrPhoto.photos;*/ this.reading = false; } }]); //別名:ctrPhotoDetail photoGallery.controller('PhotoDetailController',['$scope', '$state', function($scope,$state){ }]);
以上,通過$scope.$$childTail.ctrPhotoList在父state中的controller中拿到子state中的controller;通過$scope.$parent.ctrPhoto在子state中的controller中拿到父state中的controller。
photos-list.html
<h1>photos-list</h1> <div ng-init="ctrPhotoList.init()"> <div style="margin:auto; width: 40px;" ng-if="ctrPhotoList.reading"> <i class="fa fa-spinner fa-5x fa-pulse"></i> </div> <div class="well well-sm" ng-repeat="photo in ctrPhotoList.photos"> <div class="media"> <div class="media-left" style="width:15%;"> <a ui-sref="content.photos.detail"> <img class="img-responsive img-rounded" src="../asserts/images/{{photo.imageName}}" alt=""> </a> </div> <div class="media-body"> <h4 class="media-heading">{{photo.title}}</h4> {{photo.description}} </div> </div> </div> </div>
■ state間如何傳路由參數
在content.photos.detail這個state設置接收一個路由參數。
.state('content.photos.detail',{ url: '/detail/:id', templateUrl: 'partials/photos-detail.html', controller: 'PhotoDetailController', controllerAs: 'ctrPhotoDetail' })
photos-list.html 送出一個路由參數
<h1>photos-list</h1> <div ng-init="ctrPhotoList.init()"> <div style="margin:auto; width: 40px;" ng-if="ctrPhotoList.reading"> <i class="fa fa-spinner fa-5x fa-pulse"></i> </div> <div class="well well-sm" ng-repeat="photo in ctrPhotoList.photos"> <div class="media"> <div class="media-left" style="width:15%;"> <a ui-sref="content.photos.detail({id:photo.id})"> <img class="img-responsive img-rounded" src="../asserts/images/{{photo.imageName}}" alt=""> </a> </div> <div class="media-body"> <h4 class="media-heading">{{photo.title}}</h4> {{photo.description}} </div> </div> </div> </div>
以上,通過<a ui-sref="content.photos.detail({id:photo.id})">把路由參數送出。
controller.js PhotoDetailController控制器通過$stateParams獲取路由參數
... //別名:ctrPhotoDetail photosGallery.controller('PhotoDetailController', ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams){ var id = null; this.photo = null; this.init = function(){ id = parseInt($stateParams.id); this.photo = $scope.ctrPhoto.photos[id]; } } ]);
photos-detail.html 從以上的PhotoDetailController中獲取數據。
<h1>photo-details</h1> <a class="btn btn-default" ui-sref=".comment">通過相對路徑去子state</a> <a ui-sref="content.photos.list" style="margin-left: 15px;"> <i class="fa fa-arrow-circle-left fa-2x"></i> </a> <div ng-init="ctrPhotoDetail.init()"> <img class="img-responsive img-rounded" ng-src="../assets/images/{{ctrPhotoDetail.photo.imageName}}" style="margin:auto; width: 60%;"> <div class="well well-sm" style="margin:auto; width: 60%; margin-top: 15px;"> <h4>{{ctrPhotoDetail.photo.title}}</h4> <p>{{ctrPhotoDetail.photo.description}}</p> </div> <div style="margin:auto; width: 80%; margin-bottom: 15px;"> <button style="margin-top: 10px; width:100%;" class="btn btn-default" ui-sref=".comment">Comments</button> </div> </div> <div ui-view></