Nested States & Nested Views - kotazi/ui-router GitHub Wiki

◄ Back (State Manager)     Next (Multiple Named Views) ►

Methods for Nesting States

メソッド

States can be nested within each other. There are several ways of nesting states: ステートはお互い入れ子にすることができ、その方法はいくつかあります。

  1. Using 'dot notation'. For example .state('contacts.list', {}).

  2. Use the ui-router.stateHelper to build states from a nested state tree. Courtesy of @marklagendijk.

  3. Using the parent property with the parent name as string. For example: parent: 'contacts'

  4. Using the parent property with the parent object. For example parent: contacts (where 'contacts' is a stateObject)

  5. dot notationを利用。.state('contacts.list', {})のような。

  6. 入れ子構造からステートをビルドするためにui-router.stateHelperを使う。

  7. parentプロパティを使う(stringで)

  8. parentプロパティを使う(objectで)

Dot Notation

ドット記法

You can use dot syntax to infer your hierarchy to the $stateProvider. Below, contacts.list becomes a child of contacts.
ドット記法を使ってヒエラルキーをほのめかせます。下記ではcontants.listcontactsの子になります。

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

stateHelper module

stateHelper モジュール

This is a 3rd party module created by @marklagendijk. So you have to include it in addition to ui-router. Visit the stateHelper repo to learn more

@marklagendijkの作ったサードパーティモジュールです。 'ui.router.stateHelper'を読み込む必要あり。詳しくはこちら。

angular.module('myApp', ['ui.router', 'ui.router.stateHelper'])
  .config(function(stateHelperProvider){
    stateHelperProvider.setNestedState({
      name: 'root',
      templateUrl: 'root.html',
      children: [
        {
          name: 'contacts',
          templateUrl: 'contacts.html',
          children: [
            {
              name: 'list',
              templateUrl: 'contacts.list.html'
            }
          ]
        },
        {
          name: 'products',
          templateUrl: 'products.html',
          children: [
            {
              name: 'list',
              templateUrl: 'products.list.html'
            }
          ]
        }
      ]
    });
  });

Parent Property using State Name String

parentプロパティを文字列で

Alternately, you can specify the parent of a state via the parent property. parentプロパティでparentを指定することが可能です。

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

Object-based States

オブジェクトベースのステート

If you aren't fond of using string-based states, you can also use object-based states. The name property goes in the object and the parent property must be set on all child states, like this:

文字列型が好きじゃなければオブジェクトベースでもできます。以下省略。

var contacts = { 
    name: 'contacts',  //mandatory
    templateUrl: 'contacts.html'
}
var contactsList = { 
    name: 'contacts.list', //mandatory. This counter-intuitive requirement addressed in issue #368
    parent: contacts,  //mandatory
    templateUrl: 'contacts.list.html'
}

$stateProvider
  .state(contacts)
  .state(contactsList)

You can usually reference the object directly when using other methods and property comparisons:

$state.transitionTo(states.contacts);
$state.current === states.contacts;
$state.includes(states.contacts)

Registering States Order

ステートの順番を登録

You can register states in any order and across modules. You can register children before the parent state exists. It will queue them up and once the parent state is registered then the child will be registered.

あなたは、任意の順序でとモジュール間の状態を登録することができます。親状態が存在する前に、子供たちを登録することができます。それはそれらをキューになり、親状態はその後登録されると、子供が登録されます。

Parent MUST Exist

親は必要

If you register only a single state, like contacts.list, you MUST define a state called contacts at some point, or else no states will be registered. The state contacts.list will get queued until contacts is defined. You will not see any errors if you do this, so be careful that you define the parent in order for the child to get properly registered.

contacts.listを登録したら、必ずどこかでcontactsを定義してください。でなければステートは何も登録されません。contacts.listcontactsが定義されるまでキューをゲットします。エラーは出ませんが注意してください。

Naming Your States

ステートのネーミング

No two states can have the same name. When using dot notation the parent is inferred if it is not explicitly provided, but this doesn't change the state name. When not using dot notation, the parent must be explicitly provided, but you still cannot name any two states the same, for example you can't have two different states named "edit" even if they have different parents.

同じ名前はダメです。ドット記法の時、parrentはほのめかされます。もし、特に提供されなければ。けれどもステート名を変更しません。ドッド記法以外の時は明確にparentか提供されます。そのときも、同じ名前を付けることはできません。例えば、親の異なるステートだとしても、editを2つ持つことは許されないのです。

Nested States & Views

入れ子のステートとビュー

When the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well. Below, when the "contacts.list" state is active, the "contacts" state is implicitly active as well, because it's the parent state to "contacts.list".

あるパーティクルステート内にアプリケーションがあり、かつステートがアクティブの時、それらの先祖ステートもアクティブになります。下記の、"contacts.list"ステートがアクティブの時、"contacts"ステートもアクティブということです。"contacts.list"ステートの親だからです。

Child states will load their templates into their parent's ui-view.

子ステートは親のui-view内に、テンプレートを読み込みます。

Full Plunkr Here: http://plnkr.co/edit/7FD5Wf?p=preview

$stateProvider
  .state('contacts', {
    templateUrl: 'contacts.html',
    controller: function($scope){
      $scope.contacts = [{ name: 'Alice' }, { name: 'Bob' }];
    }
  })
  .state('contacts.list', {
    templateUrl: 'contacts.list.html'
  });

function MainCtrl($state){
  $state.transitionTo('contacts.list');
}
<!-- index.html -->
<body ng-controller="MainCtrl">
  <div ui-view></div>
</body>
<!-- contacts.html -->
<h1>My Contacts</h1>
<div ui-view></div>
<!-- contacts.list.html -->
<ul>
  <li ng-repeat="contact in contacts">
    <a>{{contact.name}}</a>
  </li>
</ul>

What Do Child States Inherit From Parent States?

子ステートは何を継承しますか?

Child states DO inherit the following from parent states:

子ステートが継承するもの:

  • リゾルブの依存関係
  • カスタムdataプロパティ

Nothing else is inherited (no controllers, templates, url, etc).

他のものは継承されません。

Inherited Resolved Dependencies

継承されたリゾルブの依存関係

New in version 0.2.0

Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states.

子ステートは親ステートからリゾルブの依存関係を継承します。これらはオーバーライドが可能です。子ステートのリゾルブ関数やコントローラにリゾルブ依存関係をインジェクトすることができます。

$stateProvider.state('parent', {
      resolve:{
         resA:  function(){
            return {'value': 'A'};
         }
      },
      controller: function($scope, resA){
          $scope.resA = resA.value;
      }
   })
   .state('parent.child', {
      resolve:{
         resB: function(resA){
            return {'value': resA.value + 'B'};
         }
      },
      controller: function($scope, resA, resB){
          $scope.resA2 = resA.value;
          $scope.resB = resB.value;
      }

NOTE:

  • The resolve keyword MUST be relative to state not views (in case you use multiple views).
  • The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.

Inherited Custom Data

Child states will inherit data properties from parent state(s), which they can overwrite.

$stateProvider.state('parent', {
      data:{
         customData1:  "Hello",
         customData2:  "World!"
      }
   })
   .state('parent.child', {
      data:{
         // customData1 inherited from 'parent'
         // but we'll overwrite customData2
         customData2:  "UI-Router!"
      }
   });

$rootScope.$on('$stateChangeStart', function(event, toState){ 
    var greeting = toState.data.customData1 + " " + toState.data.customData2;
    console.log(greeting);

    // Would print "Hello World!" when 'parent' is activated
    // Would print "Hello UI-Router!" when 'parent.child' is activated
})

Scope Inheritance by View Hierarchy Only

Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).

It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.

Abstract States

An abstract state can have child states but can not get activated itself. An 'abstract' state is simply a state that can't be transitioned to. It is activated implicitly when one of its descendants are activated.

Some examples of how you might use an abstract state are:

  • To prepend a url to all child state urls.
  • To insert a template with its own ui-view(s) that its child states will populate.
    • Optionally assign a controller to the template. The controller must pair to a template.
    • Additionally, inherit $scope objects down to children, just understand that this happens via the view hierarchy, not the state hierarchy.
  • To provide resolved dependencies via resolve for use by child states.
  • To provide inherited custom data via data for use by child states or an event listener.
  • To run an onEnter or onExit function that may modify the application in someway.
  • Any combination of the above.

Remember: Abstract states still need their own <ui-view/> for their children to plug into. So if you are using an abstract state just to prepend a url, set resolves/data, or run an onEnter/Exit function, then you'll additionally need to set template: "<ui-view/>".

Abstract State Usage Examples:

To prepend url to child state urls

$stateProvider
    .state('contacts', {
        abstract: true,
        url: '/contacts',

        // Note: abstract still needs a ui-view for its children to populate.
        // You can simply add it inline here.
        template: '<ui-view/>'
    })
    .state('contacts.list', {
        // url will become '/contacts/list'
        url: '/list'
        //...more
    })
    .state('contacts.detail', {
        // url will become '/contacts/detail'
        url: '/detail',
        //...more
    })

To insert a template with its own ui-view for child states to populate

$stateProvider
    .state('contacts', {
        abstract: true,
        templateUrl: 'contacts.html'
    })
    .state('contacts.list', {
        // loaded into ui-view of parent's template
        templateUrl: 'contacts.list.html'
    })
    .state('contacts.detail', {
        // loaded into ui-view of parent's template
        templateUrl: 'contacts.detail.html'
    })
<!-- contacts.html -->
<h1>Contacts Page</h1>
<div ui-view></div>

Combination

Shows prepended url, inserted template, paired controller, and inherited $scope object.

Full Plunkr Here: http://plnkr.co/edit/gmtcE2?p=preview

$stateProvider
    .state('contacts', {
        abstract: true,
        url: '/contacts',
        templateUrl: 'contacts.html',
        controller: function($scope){
            $scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
        }    		
    })
    .state('contacts.list', {
        url: '/list',
        templateUrl: 'contacts.list.html'
    })
    .state('contacts.detail', {
        url: '/:id',
        templateUrl: 'contacts.detail.html',
        controller: function($scope, $stateParams){
          $scope.person = $scope.contacts[$stateParams.id];
        }
    })
<!-- contacts.html -->
<h1>Contacts Page</h1>
<div ui-view></div>
<!-- contacts.list.html -->
<ul>
    <li ng-repeat="person in contacts">
        <a ng-href="#/contacts/{{person.id}}">{{person.name}}</a>
    </li>
</ul>
<!-- contacts.detail.html -->
<h2>{{ person.name }}</h2>

◄ Back (State Manager)     Next (Multiple Named Views) ►

⚠️ **GitHub.com Fallback** ⚠️