Angular UI Router 深度学习指南(译一)

这是一个深度学习指南,会带你认识UI-Router、组件及其选项。如果你只是需要一个快速入门指南,请点击访问 API Reference。

 State Manager(状态管理器)

 UI-Router中,路由设置服务 $stateProvider 的使用跟AngularJS(即Angular 1.x版本)的 angular-router 十分类似,只不过它的关注点是 state(状态)。

  • 一个state,指向整个UI和导航里某一个“地方”
  • 一个state(通过 控制器/模版/视图 属性),描述了在这个“地方”的UI应该如何呈现和工作
  • 通常情况下,一些state是有共同点,而要解决这些共性问题,主要采用状态继承(state hierarchy),也称为父/子状态(parent/child states)或嵌套状态(nested states)

The simplest form of state(简单示例)
以下是一个最简单的使用状态的例子(尤其是在module.config中配置)

<!-- index.html -->
<body ng-controller="MainCtrl">
  <section ui-view></section>
</body>
<script>

 

// app-states.js (你也可以换你喜欢的名字)
$stateProvider.state('contacts', { template: '<h1>My Contacts</h1>' })

Where does the template get inserted?(模版文件插到哪里去?)
当state被激活,对应的模版将被插入其父级 state 模板里的 ui-view 容器里,如果它是根状态(top-level state),就像上面例子中的“contacts”(没有设置父状态),那么它的父级 state 就是 index.html。
但是用“contacts”命名的state还没有被激活。所以让我们看看如何激活一个状态。

 Activating a state(激活状态)

激活状态有三种方式:

  1. 调用 $state.go() 方法。高层次的便利方法。查看更多
  2. 点击一个带有“ui-sref”的 link (超链接)。查看更多
  3. 跳转到该状态对应的 url(网站地址)。查看更多

Templates(模版)

有几种方法可以配置 state 的模板。
上面的例子使用的就是最简单的一种配置模板的方法。

$stateProvider.state('contacts', {
    template: '<h1>My Contacts</h1>'
})  

  除了直接在配置写模版 HTML 结构,你也可以加载一个 HTML 文档片段。(这是配置模板的常用方式)(译者:通过引用路径)

$stateProvider.state('contacts', {
  templateUrl: 'contacts.html'
})

templateUrl也可以是一个函数,返回值是一个 URL 地址。此函数接收一个预置参数,$stateParams。(原文此处还有半句"which is not injected",不晓得啥意思)。

$stateProvider.state('contacts', {
  templateUrl: function ($stateParams){
    return '/partials/contacts.' + $stateParams.filterBy + '.html';
  }
})

或者你可以用一个可注入的 template provider 函数,该函数能够访问局部变量,并且必须返回HTML模板,如:

$stateProvider.state('contacts', {
  templateProvider: function ($timeout, $stateParams) {
    return $timeout(function () {
      return '<h1>' + $stateParams.contactId + '</h1>'
    }, 100);
  }
})

如果你希望在状态激活(状态里的UI渲染完毕)前显示一些默认内容,你可以像下面这样使用。只要状态被激活成功,这些默认内容将会被替换成 HTML 模版。

<body>
    <ui-view>
        <i>Some content will load here!</i>
    </ui-view>
</body>

 

Controller(控制器)

你可以为你的模板绑定一个 controller 控制器。注意:如果模板没有定义,这个控制器将不会被实例化。

比如,你可以这样设置控制器:

$stateProvider.state('contacts', {
  template: '<h1>{{title}}</h1>',
  controller: function($scope){
    $scope.title = 'My Contacts';
  }
})

如果你已经在 module 中定义了一个控制器,可以这样绑定:

$stateProvider.state('contacts', {
  template: ...,
  controller: 'ContactsCtrl'
})

上面两种方式都可以用“controller as”语法:

 

$stateProvider.state('contacts', {
  template: '<h1>{{contact.title}}</h1>',
  controller: function(){
    this.title = 'My Contacts';
  },
  controllerAs: 'contact'
})
$stateProvider.state('contacts', {
  template: ...,
  controller: 'ContactsCtrl as contact'
})

另外,如果你还有更高端的需求,可以使用 controllerProvider  来动态返回一个控制器函数或者字符串标识:

$stateProvider.state('contacts', {
  template: ...,
  controllerProvider: function($stateParams) {
      var ctrlName = $stateParams.type + "Controller";
      return ctrlName;
  }
})

控制器里面可以使用 $scope.$on() 方法来监听 state 变更的事件。

只有当对应的 scopes(译者:AngularJS里面的ViewModel)被创建,控制器才会被实例化。举例来说,用户操作,使得URL变更并命中某个state,$stateProvider首先会加载这个 state 对应的模板,然后再将控制器绑定到模版的 scope 上。

Resolve(预先处理)

你可以使用 resolve 为控制器提供自定义的内容和数据。resolve 是一个可选的(控制器)依赖项 map (译:映射表,下同),会被注入到控制器中。

只要 resolve 中有依赖项会返回 promise,控制器都会进行等待。只有当所有依赖项都被解决了后,控制器才会进行实例化,同时触发 $stateChangeSuccess事件。

这个 resolve 属性是一个 map 对象,包含若干 key/value (键值对):

  • key——{string}:依赖项名称,在注入到控制器时作为形参。
  • facotry——{string|function}:
    • 如果是 string,代表一个服务名称
    • 如果是 function,那么它的返回值将会注入到控制器中。如果返回值是一个 promise,那么它将会在控制器实例化之前被解决掉,而且返回值将会注入到控制器中去。

Examples(使用示例)

每一个在resolve里面的依赖项必须在控制器被实例化之前被解决掉。注意看每一个 resolve 依赖项是如何作为参数注入到控制器中的。

$stateProvider.state('myState', {
      resolve:{

         // 示例:返回简单对象的函数
         // 因为不是promise对象,所以会被马上解决掉
         simpleObj:  function(){
            return {value: 'simple!'};
         },

         // 示例:返回promise对象的函数
         // 这是一个典型的resolve依赖项使用场景.
         // 你需要先注入所需的服务,如 $http 
         promiseObj:  function($http){
            // $http 请求某个URL返回一个promise
            return $http({method: 'GET', url: '/someUrl'});
         },

         // 示例:返回promise对象的函数. 如果你需要对结果进行处理, 使
用 then, 可以进行链式处理。
         promiseObj2:  function($http){
            return $http({method: 'GET', url: '/someUrl'})
               .then (function (data) {
                   return doSomeStuffFirst(data);
               });
         },        

         // 示例:使用 service 对应的字符串注入服务.
         // 注意,service也可以返回一个promise,使用原理如上
         translations: "translations",

         // 示例:使用返回值为 promise 的服务
         translations2: function(translations, $stateParams){
             // 假设translations服务的getLang方法返回一个promise
             return translations.getLang($stateParams.lang);
         },

         // 示例:使用返回值为 promise 的自定义服务
         greeting: function($q, $timeout){
             var deferred = $q.defer();
             $timeout(function() {
                 deferred.resolve('Hello!');
             }, 1000);
             return deferred.promise;
         }
      },

      // 该控制器函数将会在以上所有 resolve 依赖项都解决完才会进行实例化。然后这些对象将会被注入到控制器中。
      controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, translations2, greeting){
          $scope.simple = simpleObj.value;

          // You can be sure that promiseObj is ready to use!
          $scope.items = promiseObj.data.items;
          $scope.items = promiseObj2.items;

          $scope.title = translations.getLang("english").title;
          $scope.title = translations2.title;

          $scope.greeting = greeting;
      }
   })                

 

Attach Custom Data to State Objects(往state对象上挂载数据)

你可以挂载自定义数据到 state 对象上(推荐使用 data 属性以避免冲突)

// Example shows an object-based state and a string-based state
var contacts = { 
    name: 'contacts',
    templateUrl: 'contacts.html',
    data: {
        customData1: 5,
        customData2: "blue"
    }  
}
$stateProvider
  .state(contacts)
  .state('contacts.list', {
    templateUrl: 'contacts.list.html',
    data: {
        customData1: 44,
        customData2: "red"
    } 
  })

然后你可以(在控制器里)这样访问这些数据

function Ctrl($state){
    console.log($state.current.data.customData1) // outputs 5;
    console.log($state.current.data.customData2) // outputs "blue";
}

 

onEnter and onExit callbacks(回调函数 onEnter 和 onExit)

当 state 被激活或被撤销激活时,将会分别调用 ' onEnter ' 和 ‘ onExit ’ 回调函数(如果在state配置里注册了的话)。这两个回调函数都可以访问所有 resolve 依赖项。

$stateProvider.state("contacts", {
  template: '<h1>{{title}}</h1>',
  resolve: { 
     title: function () { 
       return 'My Contacts' 
     } 
  },
  controller: function($scope, title){
    $scope.title = title;
  },
  onEnter: function(title){
    if(title){ ... do something ... }
  },
  onExit: function(title){
    if(title){ ... do something ... }
  }
})

 

State Change Events(state 变化事件)

注意:在UI Router 1.0 版本中,state 变化事件已被弃用,使用 Transition Hooks 代替。

所有 state 变化时间都会在 $rootScope 层级被触发

  • $stateChangeStart——在跳转开始时被触发
    $rootScope.$on('$stateChangeStart', 
    function(event, toState, toParams, fromState, fromParams, options){ ... })

    注意

    $rootScope.$on('$stateChangeStart', 
    function(event, toState, toParams, fromState, fromParams, options){ 
        event.preventDefault(); 
        // transitionTo() promise will be rejected with 
        // a 'transition prevented' error
    })

     

  • $stateNotFound——v0.3.0 在要跳转到的状态名称无法找到时触发。该事件触发后,将会被广播,从而给予所有对应的事件接收器一次机会对错误进行处理(usually by lazy-loading the unfound state)。其中,一个特殊对象, unfoundState 将会被传入到事件处理函数中。在下面的例子中,你将会看到它所拥有的三个属性。用 event.preventDefault() 可以中止跳转(transition To() promise 将会得到 rejected 结果,结果中包含 'transition aborted' 错误)。For a more in-depth example on lazy loading states, see How To: Lazy load states
    // 在某处调用以下语句, 假设 lazy.state 状态没有被定义
    $state.go("lazy.state", {a:1, b:2}, {inherit:false});
    
    // 在另一处监听
    $rootScope.$on('$stateNotFound', 
    function(event, unfoundState, fromState, fromParams){ 
        console.log(unfoundState.to); // "lazy.state"
        console.log(unfoundState.toParams); // {a:1, b:2}
        console.log(unfoundState.options); // {inherit:false} + default options
    })

     

  • $stateChangeSuccess——当跳转完成时触发
  • $rootScope.$on('$stateChangeSuccess', 
    function(event, toState, toParams, fromState, fromParams){ ... })

    $stateChangeError——当跳转时发生错误时触发。要特别注意的是,如果在resolve函数里面发生了错误(比如 js 错误,调用的服务不存在等),依照传统,这个错误将不会抛出。你必须监听 $stateChangeError 事件来捕获期间发生的所有错误。使用 event.preventDefault() 可以防止出错时 $urlRouter 自动切换回上一个有效地址(in case of a URL navigation)。

    $rootScope.$on('$stateChangeError', 
    function(event, toState, toParams, fromState, fromParams, error){ ... })

     

View Load Events(视图加载事件)

  • $viewContentLoading——在视图(模板)开始加载时,并且是在DOM渲染完成之前触发。在 $rootScope 上广播(broadcast)该事件。
    $rootScope.$on('$viewContentLoading', 
    function(event, viewConfig){ 
        // 能够访问所有 view 配置属性.
        // 也能够访问特殊属性——'targetView'
        // 这里可以这样调用这个特殊属性,viewConfig.targetView 
    });

     

           

  • $viewContentLoaded——在视图已经加载完毕,并且DOM已经渲染完成时触发。在 $scope 上触发(emit)该事件。
    $scope.$on('$viewContentLoaded', 
    function(event){ ... });

     

转载于:https://www.cnblogs.com/bytechow/p/7967922.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值