前言
回顾上一节的内容,我们大致学习了ui-router有几个核心概念:State、Urls、Parameters、Resolve、Transitions。今天这篇文章主要讲解下State对象
State对象
ui-router提供的路由其实就是一个一个States状态对象组成的。ui-router管理这些状态之间的切换以及生命周期
每个状态等于一个URL,在进入URL之前,UI-Router首先获取任何先决条件例如:身份验证,所需数据(异步),然后激活视图并更新URL。
一个基础的state对象
const helloState = {
  name: 'hello',
  url: '/hello',
  component: HelloComponent,
  params:{},
  resolve:[]
};
name
【普通访问】
这个state的名字,我们在组件中可以通过state的name属性访问到该路由
<a uiSref="hello" uiSrefActive="active">Hello</a>
【嵌套访问】 路由在正常的项目中一般都是多级嵌套的一个树形结构的。那么我们如何定义和访问一个嵌套路由呢?
定义一个嵌套路由
const parentState = {
  name: 'folderlist',
  url: '/folder',
  component: FolderListComponent
};
const childState = {
  name: 'folderlist.messagelist',
  url: '/message',
  component: MessageListComponent
};
访问的话还是如上所诉,直接访问路由的name即可,
url
在嵌套路由的访问中应该是要把父级路由拼接起来的,例如访问childState这路由的访问地址就是http://localhost:4200/#/folder/message
常见url的形式
url: "/home"
url: "/users/:userid"
url: "/books/{bookid:[a-zA-Z_-]}"
url: "/books/{categoryid:int}"
url: "/books/{publishername:string}/{categoryid:int}"
url: "/messages?before&after"
url: "/messages?{before:date}&{after:date}"
url: "/messages/:mailboxid?{before:date}&{after:date}"
params
路由访问所需的参数
const aboutState = {
  name: 'about',
  url: '/about?id',
  component: AboutComponent,
  onEnter: function(trans, state) {
    console.log("Entering " , state , trans);
  },
  onExit: function(trans, state) {
    console.log("Leaving " + state.name);
  },
  onRetain: function(trans, state) {
    console.log(state.name + " is still active!");
  },
  params: {
    id: {
      value: 'aabcc123' // 设置默认的参数值
    }
  }
};
由于参数设置选项比较多,我们将在下一篇文章中详细讲解下params对象
resolve
resolve获取到的Data数据,可以用于当前视图 或 transition hooks,或用于该state对象其它的resolve。或者用于该state嵌套的state对象里面resolve。
因为resolve函数可以返回一个promise,所以路由器会延迟进入状态,直到promise就绪。如果任何一个承诺被拒绝,转换就会因为一个错误而中止。
【ResolvableLiteral】 一个resolve必须是一个ResolvableLiteral对象
export interface ResolvableLiteral {
    token: any; // 依赖注入的token
    resolveFn: Function; // 解析系统将在调用此函数之前异步获取依赖项
    policy?: ResolvePolicy; // policy定义何时调用解析,以及是否等待async并打开数据
    deps?: any[]; // 依赖项数组
    data?: any; // 预处理数据
}
【普通使用】
新建一个 getListResolve.ts 文件
import { ResolvableLiteral, Transition } from "@uirouter/core";
export async function resolveFn(
  trans: Transition,
) {
  return [
    {
      id: "123",
      name: "jack"
    },
    {
      id: "234",
      name: "joke"
    }
  ]
}
export const getListResolver: ResolvableLiteral = {
  token: "listData",
  deps: [Transition],
  resolveFn
};
在路由中引入:
const childState = {
  name: 'messagelist',
  parent:'folderlist',
  url: '/message',
  component: MessageListComponent,
  resolve:[getListResolver]
};
然后到MessageListComponent组件中添加DI token
import { Component, OnInit, Input } from '@angular/core';
@Component({
  selector: 'message-list',
  templateUrl: './message-list.component.html',
  styleUrls: ['./message-list.component.css']
})
export class MessageListComponent implements OnInit {
  @Input() listData; // 这样就把数据注入进来了
  constructor() { }
  ngOnInit() {
    console.log(this.listData);
  }
}
【用于其它resolve】
新建一个resolve: getMessageResolver
import { ResolvableLiteral, Transition } from "@uirouter/core";
export async function resolveFn(
  trans: Transition,
  listData
) {
  return listData.push({
    id: "456",
    name: "tom"
  })
}
export const getMessageResolver: ResolvableLiteral = {
  token: "messageData",
  deps: [Transition,"listData"],
  resolveFn
};
看下state中是如何使用的
const childState = {
  name: 'messagelist',
  parent:'folderlist',
  url: '/message',
  component: MessageListComponent,
  resolve:[getListResolver,getMessageResolver]
};
现在可以看到getMessageResolver中的deps依赖中是可以直接使用getListResolver中的token直接注入的。
【用于嵌套state中的resolve】
父路由:
const parentState = {
  name: 'folderlist',
  url: '/folder',
  component: FolderListComponent,
  resolve:[getListResolver]
};
子路由:
const childState = {
  name: 'messagelist',
  parent:'folderlist',
  url: '/message',
  component: MessageListComponent,
  resolve:[getMessageResolver]
};
getMessageResolver
import { ResolvableLiteral, Transition } from "@uirouter/core";
export async function resolveFn(
  trans: Transition,
  listData
) {
  return listData.pop();
}
export const getMessageResolver: ResolvableLiteral = {
  token: "messageData",
  deps: [Transition,"listData"],
  resolveFn
};
getMessageResolver中依赖可以直接使用getListResolver中返回的数据,这就是嵌套state中resolve的使用方法。
Abstract
抽象状态永远不能被直接激活。使用抽象状态向子状态提供继承属性(url、解析、数据等)。
const parentState = {
  name: 'folderlist',
  url: '/folder',
  abstract: true,
  component: FolderListComponent
};
这样我们直接访问:http://localhost:4200/#/folder是会报错的,上面说了:抽象状态永远不能被直接激活。
但是我们可以直接访问它的子路由,并且子路由会继承父路由的相关数据的。
parent
父路由
const parentState = {
  name: 'folderlist',
  url: '/folder',
  component: FolderListComponent
};
子路由
const childState = {
  name: 'folderlist.messagelist',
  url: '/message',
  component: MessageListComponent
};
那么我们如果要调整到这个子路由时,就必须要这样写:
<a uiSref="folderlist.messagelist" uiSrefActive="active" style="margin-right:20px;">messagelist</a>
parent属性给我们带来另外一种方式:
const childState = {
  name: 'messagelist',
  parent:'folderlist',
  url: '/message',
  component: MessageListComponent
};
现在可以这样访问:
<a uiSref="messagelist" uiSrefActive="active" style="margin-right:20px;">messagelist</a>
redirectTo
如果定义了此属性,将根据属性的值重定向
const parentState = {
  name: 'folderlist',
  url: '/folder',
  component: FolderListComponent,
  redirectTo: 'folderlist.foolist'
};
这样写的话,当我们访问folderlist路由时,会直接定向到folderlist.foolist
如果redirectTo: 'folderlist.foolist'是一个不存在的值,则该路由会不起作用
redirectTo的常用方法:
// 跳转到'A.B'路由
.state('A', {
  redirectTo: 'A.B' 
})
// 跳转到'C.D'路由,并且带上参数
.state('C', {
  redirectTo: { state: 'C.D', params: { foo: 'index' } }
})
// 跳转到路由"A"
.state('E', {
  redirectTo: () => "A"
})
// 根据参数判断要跳转的路由
.state('F', {
  redirectTo: (trans) => {
    if (trans.params().foo < 10)
      return { state: 'F', params: { foo: 10 } };
  }
})
// 向后台获取要跳转的路由
.state('G', {
  redirectTo: (trans) => {
    let svc = trans.injector().get('SomeAsyncService')
    let promise = svc.getAsyncRedirectTo(trans.params.foo);
    return promise;
  }
})
// 根据resolve Data 来判断是跳转到登录还是其它界面
.state('G', {
  redirectTo: (trans) => {
    // getAsync tells the resolve to load
    let resolvePromise = trans.injector().getAsync('SomeResolve')
    return resolvePromise.then(resolveData => resolveData === 'login' ? 'login' : null);
  }
})
hook
【onEnter】
当进入某个路由的时候触发的钩子函数
const aboutState = {
  name: 'about',
  url: '/about',
  component: AboutComponent,
  onEnter: function(trans, state) {
    console.log("Entering " + state.name);
  }
};
当进入about路由时,会触发onEnter函数
【onExit】
当离开一个路由时触发
const aboutState = {
  name: 'about',
  url: '/about',
  component: AboutComponent,
  onEnter: function(trans, state) {
    console.log("Entering " , state , trans);
  },
  onExit: function(trans, state) {
    console.log("Leaving " + state.name);
  }
};