【JS-6】
如何使用ui-router?
小課堂【武漢分院第137期】
分享人:徐恒
目錄
1.背景介紹
2.知識(shí)剖析
3.常見問題
4.解決方案
5.編碼實(shí)戰(zhàn)
6.擴(kuò)展思考
7.參考文獻(xiàn)
8.更多討論
一.背景介紹
angular路由
路由(route),幾乎所有的MVC(VM)框架都應(yīng)該具有的特性,因?yàn)樗乔岸藰?gòu)建單頁面應(yīng)用(SPA)必不可少的組成部分。
那么,對(duì)于angular而言,它自然也有內(nèi)置的路由模塊:叫做ngRoute。
不過,大家很少用它,因?yàn)樗墓δ芴邢蓿荒軡M足開發(fā)需求!!
于是,一個(gè)基于ngRoute開發(fā)的第三方路由模塊,叫做ui.router,受到了大家的“追捧”。
ngRoute vs ui.router
首先,無論是使用哪種路由,作為框架額外的附加功能,它們都將以模塊依賴的形式被引入,簡而言之就是:在引入路由源文件之后,你的代碼應(yīng)該這樣寫(以u(píng)i.router為例):
angular.module("myApp", ["ui.router"]); // myApp為自定義模塊,依賴第三方路由模塊ui.router
這樣做的目的是:在程序啟動(dòng)(bootstrap)的時(shí)候,加載依賴模塊(如:ui.router),將所有掛載在該模塊的服務(wù)(provider),指令(directive),過濾器(filter)等都進(jìn)行注冊(cè),那么在后面的程序中便可以調(diào)用了。
說到這里,就得看看ngRoute模塊和ui.router模塊各自都提供了哪些服務(wù),哪些指令?
ngRoute
$routeProvider(服務(wù)提供者) ——— 對(duì)應(yīng)于下面的urlRouterProvider和stateProvider
$route(服務(wù)) ——— 對(duì)應(yīng)于下面的urlRouter和state
$routeParams(服務(wù)) ——— 對(duì)應(yīng)于下面的stateParams
ng-view(指令) ——— 對(duì)應(yīng)于下面的ui-view
ui.router
$urlRouterProvider(服務(wù)提供者) ——— 用來配置路由重定向
$urlRouter(服務(wù))
$stateProvider(服務(wù)提供者) ——— 用來配置路由
$state(服務(wù)) ——— 用來顯示當(dāng)前路由狀態(tài)信息,以及一些路由方法(如:跳轉(zhuǎn))
$stateParams(服務(wù)) ——— 用來存儲(chǔ)路由匹配時(shí)的參數(shù)
ui-view(指令) ——— 路由模板渲染,對(duì)應(yīng)的dom相關(guān)聯(lián)
ui-sref(指令)
(注:服務(wù)提供者:用來提供服務(wù)實(shí)例和配置服務(wù)。)
這樣一看,其實(shí)ui.router和ngRoute大體的設(shè)計(jì)思路,對(duì)應(yīng)的模塊劃分都是一致的(畢竟是同一個(gè)團(tuán)隊(duì)開發(fā)),不同的地方在于功能點(diǎn)的實(shí)現(xiàn)和增強(qiáng)。
那么問題來了:ngRoute弱在哪些方面,ui.router怎么彌補(bǔ)了這些方面?
這里,列舉兩個(gè)最重要的方面來說(其他細(xì)節(jié),后面再說):
多視圖
嵌套視圖
多視圖
多視圖:頁面可以顯示多個(gè)動(dòng)態(tài)變化的不同區(qū)塊。
這樣的業(yè)務(wù)場景是有的:
比如:頁面一個(gè)區(qū)塊用來顯示頁面狀態(tài),另一個(gè)區(qū)塊用來顯示頁面主內(nèi)容,當(dāng)路由切換時(shí),頁面狀態(tài)跟著變化,對(duì)應(yīng)的頁面主內(nèi)容也跟著變化。
首先,我們嘗試著用ngRoute來做:
html
區(qū)塊1
區(qū)塊2
js$routeProvider? ? .when('/', {? ? ? ? template: 'hello world'? ? });
我們?cè)趆tml中利用ng-view指令定義了兩個(gè)區(qū)塊,于是兩個(gè)div中顯示了相同的內(nèi)容,這很合乎情理,但卻不是我們想要的,但是又不能為力,因?yàn)椋趎gRoute中:
視圖沒有名字進(jìn)行唯一標(biāo)志,所以它們被同等的處理。
路由配置只有一個(gè)模板,無法配置多個(gè)。
ok,針對(duì)上述兩個(gè)問題,我們嘗試用ui.router來做:
html
js$stateProvider? ? .state('home', {? ? ? ? url: '/',? ? ? ? views: {? ? ? ? ? ? '': {? ? ? ? ? ? ? ? template: 'hello world'? ? ? ? ? ? },? ? ? ? ? ? 'status': {? ? ? ? ? ? ? ? template: 'home page'? ? ? ? ? ? }? ? ? ? }? ? });
這次,結(jié)果是我們想要的,兩個(gè)區(qū)塊,分別顯示了不同的內(nèi)容,原因在于,在ui.router中:
可以給視圖命名,如:ui-view=”status”。
可以在路由配置中根據(jù)視圖名字(如:status),配置不同的模板(其實(shí)還有controller等)。
注:視圖名是一個(gè)字符串,不可以包含@(原因后面會(huì)說)。
嵌套視圖
嵌套視圖:頁面某個(gè)動(dòng)態(tài)變化區(qū)塊中,嵌套著另一個(gè)可以動(dòng)態(tài)變化的區(qū)塊。
這樣的業(yè)務(wù)場景也是有的:
比如:頁面一個(gè)主區(qū)塊顯示主內(nèi)容,主內(nèi)容中的部分內(nèi)容要求根據(jù)路由變化而變化,這時(shí)就需要另一個(gè)動(dòng)態(tài)變化的區(qū)塊嵌套在主區(qū)塊中。
其實(shí),嵌套視圖,在html中的最終表現(xiàn)就像這樣:
I am parent
I am child
轉(zhuǎn)成javascript,我們會(huì)在程序里這樣寫:$routeProvider? ? .when('/', {? ? ? ? template: 'I am parent
I am child
'? ? });
倘若,你真的用ngRoute這樣寫,你會(huì)發(fā)現(xiàn)瀏覽器崩潰了,因?yàn)樵趎g-view指令link的過程中,代碼會(huì)無限遞歸下去。
那么造成這種現(xiàn)象的最根本原因:路由沒有明確的父子層級(jí)關(guān)系!
看看ui.router是如何解決這一問題的?
$stateProvider? ? .state('parent', {? ? ? ? abstract: true,? ? ? ? url: '/',? ? ? ? template: 'I am parent
'? ? })? ? .state('parent.child', {? ? ? ? url: '',? ? ? ? template: 'I am child'? ? });
巧妙地,通過parent與parent.child來確定路由的父子關(guān)系,從而解決無限遞歸問題。
另外子路由的模板最終也將被插入到父路由模板的div[ui-view]中去,從而達(dá)到視圖嵌套的效果。
記住下面是ui.router用到的所有指令
ui.router
$urlRouterProvider(服務(wù)提供者) ——— 用來配置路由重定向
$urlRouter(服務(wù))
$stateProvider(服務(wù)提供者) ——— 用來配置路由
$state(服務(wù)) ——— 用來顯示當(dāng)前路由狀態(tài)信息,以及一些路由方法(如:跳轉(zhuǎn))
$stateParams(服務(wù)) ——— 用來存儲(chǔ)路由匹配時(shí)的參數(shù)
ui-view(指令) ——— 路由模板渲染,對(duì)應(yīng)的dom相關(guān)聯(lián)
ui-sref(指令)
初級(jí)應(yīng)用(會(huì)用就行)
介紹用這個(gè)例子($urlRouter和$stateParams沒用到,其他都用到:$urlRouterProvider,$stateProvider,$state,ui-view,ui-sref)
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'
})
})
首先給大家介紹angular-ui-router的基本用法。
如何引用依賴angular-ui-router就是
再從簡單的開始(其實(shí)這幾個(gè)服務(wù)也都是有依賴的,例如$urlRouterProvider依賴$urlMatcherFactoryProvider? $locationProvider。$urlRouterProvider依賴$urlRouterProvider $urlMatcherFactoryProvider。但初級(jí)應(yīng)用這不展開,只講要用到某幾個(gè)方法(不是全部))
$urlRouteProvider(總共3個(gè)when,otherwise和rule)這里懂when和otherwise就行。
$urlRouteProvider.when(whenPath, toPath)
為給定的URL匹配注冊(cè)一個(gè)處理程序。
$urlRouterProvider.otherwise(path)
定義一個(gè)當(dāng)請(qǐng)求的路徑是無效路徑時(shí)跳轉(zhuǎn)的路徑。
例子:就用到一種情況(當(dāng)請(qǐng)求的路徑是無效路徑時(shí)跳轉(zhuǎn)的路徑)
$urlRouterProvider
.otherwise('/login');
// .when("","/login");
$stateProvider(總共2個(gè)方法:decorator和state。但這里只講state(state這個(gè)里面的配置也不講全的,只講幾個(gè)暫時(shí)用到的))
處理路由狀態(tài)的服務(wù),路由的狀態(tài)反映了該項(xiàng)在應(yīng)用程序中的位置,描述了在當(dāng)前狀態(tài)下UI是應(yīng)該怎么樣的,并且該做什么。
方法:
state(name,stateConfig);
注冊(cè)一個(gè)狀態(tài),并給定其配置。
//state可以有子父級(jí)
$stateProvider.state("home",{});
$stateProvider.state("home.child",{})
//state可以是鏈?zhǔn)降?/p>
$stateProvider.state("home",{}).state("about",{}).state("photos",{});
參數(shù):
name:狀態(tài)的名稱。
stateConfig:狀態(tài)配置對(duì)象。配置具有以下各項(xiàng)屬性(全部屬性,看一下好了):
template: string/function,html模板字符串,或者一個(gè)返回html模板字符串的函數(shù)。
templateUrl:string/function,模板路徑的字符串,或者返回模板路徑字符串的函數(shù)。
templateProvider:function,返回html模板字符串或模板路徑的服務(wù)。
controller:string/function,新注冊(cè)一個(gè)控制器函數(shù)或者一個(gè)已注冊(cè)的控制器的名稱字符串。
controllerProvider:function,返回控制器或者控制器名稱的服務(wù)
controllerAs:string,控制器別名。
parent:string/object,手動(dòng)指定該狀態(tài)的父級(jí)。
resolve:object,將會(huì)被注入controller去執(zhí)行的函數(shù),string,function>形式。
url:string,當(dāng)前狀態(tài)的對(duì)應(yīng)url。
views:object,視圖展示的配置。string,object>形式。
abstract:boolean,一個(gè)永遠(yuǎn)不會(huì)被激活的抽象的狀態(tài),但可以給其子級(jí)提供特性的繼承。默認(rèn)是true。
onEnter:function,當(dāng)進(jìn)入一個(gè)狀態(tài)后的回調(diào)函數(shù)。
onExit:function,當(dāng)退出一個(gè)狀態(tài)后的回調(diào)函數(shù)。
reloadOnSearch:boolean,如果為false,那么當(dāng)一個(gè)search/query參數(shù)改變時(shí)不會(huì)觸發(fā)相同的狀態(tài),用于當(dāng)你修改$location.search()的時(shí)候不想重新加載頁面。默認(rèn)為true。
data:object,任意對(duì)象數(shù)據(jù),用于自定義配置。繼承父級(jí)狀態(tài)的data屬性。換句話說,通過原型繼承可以達(dá)到添加一個(gè)data數(shù)據(jù)從而整個(gè)樹結(jié)構(gòu)都能獲取到。
params:url里的參數(shù)值,通過它可以實(shí)現(xiàn)頁面間的參數(shù)傳遞。
這里用到了
url:string,當(dāng)前狀態(tài)的對(duì)應(yīng)url。
params:url里的參數(shù)值,通過它可以實(shí)現(xiàn)頁面間的參數(shù)傳遞。
template: string/function,html模板字符串,或者一個(gè)返回html模板字符串的函數(shù)。
templateUrl:string/function,模板路徑的字符串,或者返回模板路徑字符串的函數(shù)。
resolve:object,將會(huì)被注入controller去執(zhí)行的函數(shù),string,function形式。
例子:
$stateProvider
.state("login",{
url: "/login",
templateUrl: "tpls/login.html",
resolve:{
load:['$ocLazyLoad',function($ocLazyLoad){
return $ocLazyLoad.load([
'css/login.css',
'js/login.js'
]);
}]
}
})
進(jìn)一步
.state("main.article-list",{
url: "/article-list/:page/:size/:startAt/:endAt/:type/:status",
params:{'page':"1",'size':"10"},
templateUrl: "tpls/article-list.html",
resolve:{
load:['$ocLazyLoad',function($ocLazyLoad){
return $ocLazyLoad.load([
'css/article-list.css',
'js/article-list.js'
]);
}]
}
})
再說下
$state(總共6個(gè)方法和1個(gè)事件:go,href,include,is,reload,transitionTo和事件:$stateChangeError,$stateChangeStart,$stateChangeSuccess和$stateNotFound。這里只要會(huì)go就行)
$state服務(wù)負(fù)責(zé)代表狀態(tài)及提供狀態(tài)之間的轉(zhuǎn)換。它還提供你當(dāng)前的狀態(tài)及上一個(gè)狀態(tài)。
方法:
go(to,params,options);
參數(shù):
to:string,即將跳轉(zhuǎn)的狀態(tài)。
params:object,跳轉(zhuǎn)所帶的參數(shù)。
options:object,可選配置對(duì)象。有 location(是否更新地址欄的url,或以什么字符串替換url),inherit(是否繼承當(dāng)前url的參數(shù)),relative(當(dāng)變化相對(duì)路徑:如"^,定義的狀態(tài)是相對(duì)的),notify(是否廣播$stateChangeStart和$stateChangeSuccess事件),reload(是否重新載入)。
實(shí)例:
$state.go("login");
跳轉(zhuǎn)到在前面中定義的狀態(tài)那里
$stateProvider
.state("login",{})
然后就是^和.的用法區(qū)別,一個(gè)是向上一級(jí),一個(gè)向下一級(jí)
$state.go('photos.detail')
$state.go('^')到上一級(jí),比如從photo.detail到photo
$state.go('^.list')到相鄰state,比如從photo.detail到photo.list
$state.go('^.detail.comment')到孫子級(jí)state,比如從photo.detail到photo.detial.comment
ui-sref(2種,一種不帶參數(shù),一種帶)
一種將鏈接(a標(biāo)簽)綁定到一個(gè)狀態(tài)的指令。點(diǎn)擊該鏈接將觸發(fā)一個(gè)可以帶有可選參數(shù)的狀態(tài)轉(zhuǎn)換。
ui-sref="stateName"ui-sref="stateName({param:value,param:value})"代碼:首頁你的主頁
ui-view
一種是沒有名字的
一種是有名字的
來一個(gè)demo1
項(xiàng)目文件結(jié)構(gòu)
node_modules/ //這里放各種依賴
partials/? ? //這里放跳的頁面
.....about.html
.....home.html
.....photos.html
app.js? ? ? //寫router
index.html
index.html文件? 就是一個(gè)導(dǎo)航欄(注意這里有ui-sref的跳轉(zhuǎn))+一個(gè)ui-view(當(dāng)然是跳轉(zhuǎn)后的各個(gè)頁面的東西)+依賴
app.js? 寫的ui.router
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'
})
})
更進(jìn)一步就是task6-10(低版本)
router.js
var myApp = angular.module("myApp",['ui.router','oc.lazyLoad','ngMessages','tm.pagination']);
myApp.config(function ($stateProvider,$urlRouterProvider) {
$urlRouterProvider
.otherwise('/login');
// .when("","/login");
$stateProvider
.state("main.article-list",{
url: "/article-list/:page/:size/:startAt/:endAt/:type/:status",
params:{'page':"1",'size':"10"},
templateUrl: "tpls/article-list.html",
resolve:{
load:['$ocLazyLoad',function($ocLazyLoad){
return $ocLazyLoad.load([
'css/article-list.css',
'js/article-list.js'
]);
}]
}
})
});
上面用到了url的參數(shù),params的參數(shù),以及resolve。當(dāng)然后面會(huì)用到$stateParams。
url中的/:page/:size/在params中初始化,然后傳參數(shù)過去。resolve中的是懶加載。
上面URL傳過來的參數(shù)在article-list.js中用到(分頁插件用的),通過$stateParams獲取。
article-list.js
var listApp = angular.module("myApp",[]);
listApp.controller("listCtrl",['$scope','$http','$state','$stateParams',function ($scope,$http,$state,$stateParams) {
// 分頁部分
$scope.paginationConf = {
showFlag:0,
// 當(dāng)前頁
currentPage: 1,
// 每頁默認(rèn)
itemsPerPage: 10,
// 點(diǎn)擊每個(gè)分頁按鈕都會(huì)觸發(fā)這個(gè)函數(shù),然后刷新加載
onChange:function () {
console.log('$scope.paginationConf.currentPage=' );
console.log($scope.paginationConf.currentPage );
console.log('$scope.paginationConf.itemsPerPage=');
console.log($scope.paginationConf.itemsPerPage);
$state.go('main.article-list', {
page: $scope.paginationConf.currentPage ,
size: $scope.paginationConf.itemsPerPage
}, {reload: true});
}};
console.log('$stateParams=' );
console.log($stateParams );
$scope.paginationConf.currentPage = $stateParams.page ;
$scope.paginationConf.itemsPerPage = $stateParams.size ;
二.知識(shí)剖析
三.常見問題
四.解決方案
五.代碼實(shí)戰(zhàn)
六.拓展思考
七.參考文獻(xiàn)
angular的uiRouter服務(wù)學(xué)習(xí)
八.更多討論
鳴謝
感謝大家觀看
BY : 徐恒
1.ngRoute能做到多視圖嵌套么
2.ng-view能命名么
3.ui-router這么多參數(shù)常用的是那些?
4.還有哪些情況下是適合ngRoute使用的
------------------------------------------------------------------------------------------------------------------------
技能樹.IT修真院
“我們相信人人都可以成為一個(gè)工程師,現(xiàn)在開始,找個(gè)師兄,帶你入門,掌控自己學(xué)習(xí)的節(jié)奏,學(xué)習(xí)的路上不再迷茫”。
這里是技能樹.IT修真院,成千上萬的師兄在這里找到了自己的學(xué)習(xí)路線,學(xué)習(xí)透明化,成長可見化,師兄1對(duì)1免費(fèi)指導(dǎo)。快來與我一起學(xué)習(xí)吧 !