AngularJS中ui-router全攻略

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AngularJS中ui-router全攻略相关的知识,希望对你有一定的参考价值。

 

首先是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:{
        "[email protected]":{templateUrl: ‘partials/photos.html‘}
    }
})

 

所有state下views下的所有键值对(类似 "[email protected]":{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‘},
                "[email protected]":{templateUrl: ‘partials/header.html‘},
            }
        })
        .state(‘content.home‘,{
            url: ‘home‘,
            views:{
                "[email protected]":{templateUrl: ‘partials/home.html‘}
            }
        })
        .state(‘content.photos‘,{
            url: ‘photos‘,
            views:{
                "[email protected]":{templateUrl: ‘partials/photos.html‘}
            }
        })
        .state(‘content.about‘,{
            url:‘about‘,
            views:{
                "[email protected]":{templateUrl: ‘partials/about.html‘}
            }
        })
})

 

这时候,页面是这样呈现出来的:

→ 来到home这个路由

 

.state(‘content.home‘,{
    url: ‘home‘,
    views:{
        "[email protected]":{templateUrl: ‘partials/home.html‘}
    }
})

 

以上,告诉我们partials/home.html将会被加载到与"[email protected]"匹配的ui-view中。暂时对应的ui-view还没有出现,于是等待。

→ 路由看到index.html上的<div ui-view></div>

 

.state(‘content‘,{
    url: ‘/‘,
    views:{
        "":{templateUrl: ‘partials/content.html‘},
        "[email protected]":{templateUrl: ‘partials/header.html‘},
    }
})

 

于是,就找到了content这个state下views下的 "":{templateUrl: ‘partials/content.html‘}这个键值对,把partials/content.html显示出来。

→ 分别加载partials/content.html页面上的各个部分

看到<div ui-view="header"></div>,就加载如下:

"[email protected]":{templateUrl: ‘partials/header.html‘},


看到<div ui-view="body"></div>,先加载 "[email protected]":{templateUrl: ‘partials/home.html‘}


→ 点击header上的链接

点击<a ui-sref="content.photos">Photos</a>,来到:

.state(‘content.photos‘,{
    url: ‘photos‘,
    views:{
        "[email protected]":{templateUrl: ‘partials/photos.html‘}
    }
})

 

把partials/photos.html显示到<div ui-view="body"></div>中去。


点击<div ui-view="body"></div>,来到:

.state(‘content.about‘,{
    url:‘about‘,
    views:{
        "[email protected]":{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‘},
                "[email protected]":{templateUrl: ‘partials/header.html‘},
            }
        })
        .state(‘content.home‘,{
            url: ‘home‘,
            views:{
                "[email protected]":{templateUrl: ‘partials/home.html‘}
            }
        })
        .state(‘content.photos‘,{
            url: ‘photos‘,
            views:{
                "[email protected]":{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:{
                "[email protected]":{templateUrl: ‘partials/about.html‘}
            }
        })
})

 

■ 抽象state

如果一个state,没有通过链接找到它,那就可以把这个state设置为abstract:true,我们把以上的content和content.photos这2个state设置为抽象。

 

.state(‘content‘,{
    url: ‘/‘,
    abstract: true,
    views:{
        "":{templateUrl: ‘partials/content.html‘},
        "[email protected]":{templateUrl: ‘partials/header.html‘},
    }
})
...


.state(‘content.photos‘,{
    url: ‘photos‘,
    abstract: true,
    views:{
        "[email protected]":{templateUrl: ‘partials/photos.html‘}
    }
})

 

那么,当一个state设置为抽象,如果通过ui-sref或路由导航到该state会出现什么结果呢?

--会导航到默认路由上

$urlRouterProvider.otherwise(‘home‘);

 

.state(‘content.home‘,{
    url: ‘home‘,
    views:{
        "[email protected]":{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‘},
                "[email protected]":{templateUrl: ‘partials/header.html‘},
            }
        })
        .state(‘content.home‘,{
            url: ‘home‘,
            views:{
                "[email protected]":{
                    templateUrl: ‘partials/home.html‘,
                    controller: ‘HomeController‘,
                    controllerAs: ‘ctrHome‘
                }
            }
        })
        .state(‘content.photos‘,{
            url: ‘photos‘,
            abstract: true,
            views:{
                "[email protected]":{
                    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:{
                "[email protected]":{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="c

以上是关于AngularJS中ui-router全攻略的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AngularJS 的 ui-router 提取查询参数?

AngularJS:ui-router解析后无法访问控制器中的变量

AngularJs:ui-router 视图中的 ag-grid (templateUrl)

为啥这个 angularjs ui-router 代码会使我的浏览器崩溃?

尽管ui-route和provider似乎工作正常,但我无法在angularjs中加载视图......这是一段代码

AngularJS ui-router (嵌套路由)