Angular-Ui-Router

璩涵衍
2023-12-01

什么是Angular-Ui-Router

它是Angular第三方开发用于管理ui与路由的模块,功能比Angular原生的ngRoute要强而且全,因此原生基本已经被其替代。
UI-Router是采用的是一种状态管理机制,“状态”可以继承,“状态”不禁包含url,还有views,controller等,以此来组织路由和控制界面UI的渲染,而不是单纯的改变路由。对于UI方面提供嵌套视图,可以一个页面嵌套多个视图,多视图再嵌套单个视图,每个视图可提供其特定的controller等针对管理,方便视图重用且功能精细,可以打造十分复杂的web应用。

基本搭建

Angular自身是通过集成各个功能模块而形成完整的web应用,所以要使用Ui-Router除了angular.js只用导入其模块angular-ui-router.js(也可以加产品版本min.js),可以通过bower包管理器或github等下载。

<script src="angular-ui-router/release/angular-ui-router.js"></script>

然后在ngApp中加入ui.router

var app = angular.module('myApp', ['ui.router']);

然后再创建login.html用于嵌套,再在需要嵌套的页面配置

<!-index.html url'/'-->
<div ui-view></div>

再配置路由

app.config(function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');
    $stateProvider
        .state('login', {
            url: '/',
            templateUrl: 'login.html',
        });
}); 

当进入主页它便自动将login.html嵌套到ui-view中。

ui-view

ui-view是用来告诉ui-router哪里需要嵌套模板

<div ui-view></div>
<div ui-view="chart"></div> 
<div ui-view="data"></div> 
$stateProvider.state("home", {
    views: {
        "": {
            template: "<h1>HELLO!</h1>"
        },
        "chart": {
            template: "<chart_thing/>"
        },
        "data": {
            templateUrl: 'tplUrl.html'
        }
    }    
})

autoscroll

让ui-view有滚动条效果,<ui-view autoscroll[='expression']/>expression可选

ui-sref

绑定在<a>tag,用于‘状态’转换,类似<a>的href属性。底层通过$state.href()来返回编译好的状态填充。
Usage:
- ui-sref=’stateName’
- ui-sref=’stateName({param: value, param: value})’

Example:

<a ui-sref="home">Home</a> | <a ui-sref="about">About</a>

<ul>
    <li ng-repeat="contact in contacts">
        <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
    </li>
</ul>

$stateProvider

$stateProvider用于配制’状态‘,可以是链式配制。

$stateProvider.state(stateName1, stateConfig1)
              .state(stateName2,stateConfig2);

stateName是以个唯一的字符串,用于标记状态。
stateConfig用于管理状态的配制,object类型。

stateConfig部分内容:

关于url
param必须是拉丁字母、数字或下滑线,而且必须是唯一的。
- ‘/hello’
- ‘/user/:id’ :匹配’/user/bob’ 或’/user/1234!!!’或甚至是’/user/’ 但不是能’/user’ 或 ‘/user/bob/details’
- ‘/user/{id}’:跟上面一样,只是将:换成{}
- ‘/user/{id:int}’:指定param是Interger类型

正则表达形式

url: "/contacts/{contactId:[0-9]{1,8}}"

指定查询参数

url: "/contacts?myParam"
// will match to url of "/contacts?myParam=value"

路由嵌套

$stateProvider
  .state('contacts', {
     url: '/contacts',
     ...
  })
  .state('contacts.list', {
     url: '/list',
     ...
  });

contact 状态配对/contacts
contacts.list状态配对/contacts/list
若在contact.list状态中url:'^/list',则可直接匹配”/list”

关于指定html模板,为ui-view指定加载指定模板

  • template:可以是字符串html内容,也可以是返回html字符串的方法。
  • templateUrl:html模板的路径,也可以是返回该路径的方法
  • templateProvider :返回html字符串方法,该方法可以注入,可以访问本地。

关于指定的controller,为指定模板添加controller,很便捷

  • controller:实现方法或controller的名字。
  • controllerProvider:实现可注入方法返回普通的controller或字符串 。

关于params,若不在url配制参数也可以在params配制,是个object类型,该参数挂载在$stateParams。在传递参数时一定要在url或params先定义有这个参数。

关于views,views是个object类型,用来代替上述所说的template,通过状态的配制达到多视图嵌套单个页面。
Example:

<body>
  <div ui-view="filters"></div>
  <div ui-view="tabledata"></div>
  <div ui-view="graph"></div>
</body>
$stateProvider
  .state('report',{
    views: {
      'filters': {
           templateUrl: 'report-filters.html',
           controller: function($scope){
            ... controller stuff just for filters view ... 
            }
      },
      'tabledata': {
            templateUrl: 'report-table.html',
            controller: function($scope){
             ... controller stuff just for tabledata view ... 
             }
      },
      'graph': {
            templateUrl: 'report-graph.html',
            controller: function($scope){
             ... controller stuff just for graph view ... 
             }
          }
      }
  });

通过viewname@statename可以在嵌套在页面的视图中再嵌套视图。
Example:

<!- index.html-->
<div ui-view></div>
<!-main.html-->
<div>
    <div>
        <div ui-view="childView1"></div>
    </div>
    <div>
        <div ui-view="childView2"></div>
    </div>
</div>
$stateProvider
        .state('main', {
            url: '/',
            views: {
                '': {
                    templateUrl: 'main.html',
                },
                'childView1@main': {
                    templateUrl: 'childView1.html',
                    controller: 'child1Ctrl'
                },
                'childView2@main': {
                    templateUrl: 'childView2.html',
                    controller: 'child2Ctrl'
                }
            }
        })

当跳转到url ‘/’就可以渲染index.html,包括内嵌main.html,而child1.html与child2.html则内嵌于main.html中。

关于Resolve,用来为controller提供数据或内容,将resolve内的对象当做参数注入

resolve:{

         // Example using function with simple return value.
         // Since it's not a promise, it resolves immediately.
         simpleObj:  function(){
            return {value: 'simple!'};
         },

         // Example using function with returned promise.
         // This is the typical use case of resolve.
         // You need to inject any services that you are
         // using, e.g. $http in this example
         promiseObj:  function($http){
            // $http returns a promise for the url data
            return $http({method: 'GET', url: '/someUrl'});
         },

         // Another promise example. If you need to do some 
         // processing of the result, use .then, and your 
         // promise is chained in for free. This is another
         // typical use case of resolve.
         promiseObj2:  function($http){
            return $http({method: 'GET', url: '/someUrl'})
               .then (function (data) {
                   return doSomeStuffFirst(data);
               });
         },        

         // Example using a service by name as string.
         // This would look for a 'translations' service
         // within the module and return it.
         // Note: The service could return a promise and
         // it would work just like the example above
         translations: "translations",

         // Example showing injection of service into
         // resolve function. Service then returns a
         // promise. Tip: Inject $stateParams to get
         // access to url parameters.
         translations2: function(translations, $stateParams){
             // Assume that getLang is a service method
             // that uses $http to fetch some translations.
             // Also assume our url was "/:lang/home".
             return translations.getLang($stateParams.lang);
         },

         // Example showing returning of custom made promise
         greeting: function($q, $timeout){
             var deferred = $q.defer();
             $timeout(function() {
                 deferred.resolve('Hello!');
             }, 1000);
             return deferred.promise;
         }
      },

      // The controller waits for every one of the above items to be
      // completely resolved before instantiation. For example, the
      // controller will not instantiate until promiseObj's promise has 
      // been resolved. Then those objects are injected into the controller
      // and available for use.  
      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;
      }
   })

关于abstract 抽象模板,抽象模板不能被激活,但是它的子模板可以被激活。抽象模板可以提供一个包括了多个有名的视图的模板,或者它可以传递作用域变量$scope给子模板。使用它可以在同一个url下传递自定义数据或者预载入的依赖,abstract模板需要ui-view,以下是包含多视图模板:

$stateProvider
  .state('admin', {
    abstract: true,
    url: '/admin',
    template: '<div ui-view></div>'
  })
  .state('admin.index', {
    url: '/index',
    template: '<h3>Admin index</h3>'
  })
  .state('admin.users', {
    url: '/users',
    template: '<ul>...</ul>'
  });

onEnter,onExit,在进入状态或脱离状态时,要做什么可以使用,比如离开时显示是否要离开的对话框等。

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

reloadOnSearch,如果reloadOnSearch选项被设置为true(默认),当$location.search()发生变化时会重新加载路由。如果设置为false,那么当url中的查询部分发生变化时就不会重新加载路由。

data,用于在stateconfig自定义数据.该数据和预载入数据resolve属性相似,但是该数据不会被注入到控制器中,promise也不会被预载入,它的用途是从父状态传递数据到子状态。

// 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"
    } 
  })

$urlRouterProvider

可以通过不同的 url 激活不同的状态,由于设定好的状态在特定的url被访问是会自动激活,所以在管理激活和加载状态的时候, $urlRouterProvider 并不是必须的。在状态管理之外的时候才会需要,比如重定向,或者验证的时候。

when

该函数需要两个参数:1.当前的路径(String | RegExp | UrlMatcher(定义url ),2.需要重定向到的路径(或者是需要在路径被访问是运行的函数)。设置重定向前需要为$urlRouterProvider设置when函数来接受一个字符串.
handler是路径:

app.config(function($urlRouterProvider){
    // when there is an empty route, redirect to /index   
    $urlRouterProvider.when('', '/index');

    // You can also use regex for the match parameter
    $urlRouterProvider.when(/aspx/i, '/index');
})

hander是函数:

$urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
    if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
        $state.transitionTo(state, $match, false);
    }
}]);

如果提供一个函数处理,路由匹配的时候,这个函数就会被调用,它可以返回下列三种之一的结果。

  • false,这个回应告诉 $urlRouter 规则并不匹配,应该查找其它匹配的状态,在我们希望验证用户是否访问正确地址的时候很有用。
  • 字符串,$urlRouter 将其作为重定向目标。
  • true 或者 undefined,函数已经处理了这个 url

otherwise

oterwiese() 在没有其它路由匹配的情况下重定向。这是创建默认 url 的好方法。otherwise() 函数只需要一个参数,一个字符串或者一个函数。如果提供了一个字符串,就会被看做一个默认地址,在任何错误的或者不能匹配任何路由的时候,就会被重定向到这个地址。如果是一个函数,在没有其它路由匹配的时候,就会被执行

app.config(function($urlRouterProvider){
    // if the path doesn't match any of the urls you configured
    // otherwise will take care of routing the user to the specified url
    $urlRouterProvider.otherwise('/index');

    // Example of using function rule as param
    $urlRouterProvider.otherwise(function($injector, $location){
        ... some advanced code...
    });
})

rule

如果我们希望处理任何路由,或者在其它路由之前进行一些处理,可以使用 rule() 函数。

app.config(function ($urlRouterProvider) {
   // Here's an example of how you might allow case insensitive urls
   // Note that this is an example, and you may also use 
   // $urlMatcherFactory.caseInsensitive(true); for a similar result.
   $urlRouterProvider.rule(function ($injector, $location) {
       //what this function returns will be set as the $location.url
        var path = $location.path(), normalized = path.toLowerCase();
        if (path != normalized) {
            //instead of returning a new url string, I'll just change the $location.path directly so I don't have to worry about constructing a new url string and so a new state change is not triggered
            $location.replace().path(normalized);
        }
        // because we've returned nothing, no state change occurs
    });
})

$state

$state.go

$state.go可以在javascript中转换状态(类似ui-sref,在html中转换)。

$state.go(to, [,toParams],[,options])

形参to是stateName,必须,使用”^”或”.”表示相对状态;
形参toParams可空,类型是对象,挂载在对应$stateparams,参数也可以通过状态机制继承;
形参options可空,类型是对象,字段包括:

  • location:为boolean类型默认true,如果是true会更新地址中的url,flase不会,若为replace会更新并覆盖最后一此的记录。
  • inherit为boolean类型默认true,如果是true会继承最近的的url中的参数。
  • relative为对象默认$state.$current,当定义为相对路劲时,定义那个状态是相对的。
  • notify为boolean类型默认为true, 当为true时广播 $stateChangeStart 和
    $stateChangeSuccess
    事件
  • reload为boolean类型默认为false,如果是true会强制重载且一切一样。

Example:

$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

$state.reload()

强制重载当前状态,与$state.go的option内配制reload=true类似。

$state.includes(stateName [, params])

$state.includes方法用于判断当前激活状态是否是指定的状态或者是指定状态的子状态,返回时boolean值.

比如当前状态是contacts.details.item

$state.includes("contacts"); // returns true
$state.includes("contacts.details"); // returns true
$state.includes("contacts.details.item"); // returns true
$state.includes("contacts.list"); // returns false
$state.includes("about"); // returns false

params,假设当前状态”contacts.details.item.edit”,其url是/contacts/1/address/edit,为:id配制1 和 :item配制’address’。

$state.includes("contacts.detail", {id: 1}); // returns true
$state.includes("contacts.detail.item", {item:'address'}); // returns true
$state.includes("contacts", {bogus:'gnarly'}); // returns false

$state.is(stateOrName [, params])

类似$state.includes,但它比较严格。
假设当前状态:contact.details.item

$state.is("contact.details.item"); // returns true
$state.is(contactDetailItemStateConfigObj); // returns true
// Everything else would return false

params:

$state.is("contacts.detail.item.edit", {id: 1, item: 'address'}); // returns true
// Everything else returns `false`

$state.href(stateOrName [, params] [, options])

一个URL生成方法,返回已经填充指定参数的状态的编译后的链接。

$state.href("about.person", { person: "bob" })

$state.get([stateName])

得到状态的配制object

$state.current

返回状态对象的引用,用于访问自定义data

注意:使用$State$stateparams需要将其挂载到$rootscope

angular.module("myApp").run(function ($rootScope, $state, $stateParams) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;
});

$urlRouter

$urlRouter.sync()

Returns null.

Triggers an update; the same update that happens when the address bar url changes, aka $locationChangeSuccess. This method is useful when you need to use preventDefault() on the $locationChangeSuccess event, perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed with the transition by calling $urlRouter.sync().

angular.module('app', ['ui.router']);
  .run(function($rootScope, $urlRouter) {
    $rootScope.$on('$locationChangeSuccess', function(evt) {
      // Halt state change from even starting
      evt.preventDefault();
      // Perform custom logic
      var meetsRequirement = /* ... */
      // Continue with the update and state transition if logic allows
      if (meetsRequirement) $urlRouter.sync();
    });
  });

$stateParams

用于获取url中的参数,每个url的参数都放在对应$stateParams这个服务中,是一个object类型,将其注入controller或service中可以拿到url的参数。

$stateProvider.state('contacts.detail', {
   url: '/contacts/:contactId',   
   controller: function($stateParams){
      $stateParams.contactId  //*** Exists! ***//
   }
}).state('contacts.detail.subitem', {
   url: '/item/:itemId', 
   controller: function($stateParams){
      $stateParams.contactId //*** Watch Out! DOESN'T EXIST!! ***//
      $stateParams.itemId //*** Exists! ***//  
   }
})

使用resolve传递父参数

$stateProvider.state('contacts.detail', {
   url: '/contacts/:contactId',   
   controller: function($stateParams){
      $stateParams.contactId  //*** Exists! ***//
   },
   resolve:{
      contactId: ['$stateParams', function($stateParams){
          return $stateParams.contactId;
      }]
   }
}).state('contacts.detail.subitem', {
   url: '/item/:itemId', 
   controller: function($stateParams, contactId){
      contactId //*** Exists! ***//
      $stateParams.itemId //*** Exists! ***//  
   }
})

$uiViewScroll

$uiViewScroll(elem):会为element添加scroll

Filters

和ng-class,ng-if等angular指令绑定一起用

stateName" | isState 等于 $state.is("stateName")
"stateName" | includedByState 等于 $state.includes("stateName")

Events

所有的事件都从$rootscope传播

$stateChangeStart

当状态开始转变时触发

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

使用preventDefault()来预防意外事件

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

$stateNotFound

使用unfoundState对象来捕捉属性

// somewhere, assume lazy.state has not been defined
$state.go("lazy.state", {a:1, b:2}, {inherit:false});

// somewhere else
$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

用于catchresolvefunction中异常,并使用event.preventDefault()来预防$UrlRouter重新调回之前的有效url

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

View Load Events

$viewContentLoading

在页面加载时候触发,在DOM描绘之前,$rootscope广播这事件

$rootScope.$on('$viewContentLoading', 
function(event, viewConfig){ 
    // Access to all the view config properties.
    // and one special property 'targetView'
    // viewConfig.targetView 
});

$viewContentLoaded

视图加载完,在dom描绘之后,这个视图对应$scope触发该事件

$scope.$on('$viewContentLoaded', 
function(event){ ... });

参考文档

https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-view

 类似资料: