admin 管理员组

文章数量: 1086019

I have an example here. How i can access 'itemId' parameter in the parent state controller? Parent view must not be reloaded.

angular.module('app',['ui.router'])
.config(function($stateProvider){
   $stateProvider.state('step', {
            url: '/orders/:step',
            templateUrl: 'parent.html',
            controller: function ($scope, $stateParams) {
                $scope.itemId = $stateParams.itemId;
                $scope.step = $stateParams.step;
            }
        })

        .state('step.item', {
            url: '/:itemId',
            templateUrl: 'child.html',
            controller: function ($scope, $stateParams) {
                $scope.itemId = $stateParams.itemId;
                $scope.step = $stateParams.step;
            }

        });
}).controller('SimpleController', function($scope, $state){
  $scope.items = [1,2,3,4,5,6];
  $scope.steps = ["first", "second"];
})

I see only two ways:

  1. add an service and inject it to both controllers
  2. access a parent scope from a child controller and pass a parameter

    But both cause me to add watchers at a parent scope. Maybe i can acplish it easier?

I have an example here. How i can access 'itemId' parameter in the parent state controller? Parent view must not be reloaded.

angular.module('app',['ui.router'])
.config(function($stateProvider){
   $stateProvider.state('step', {
            url: '/orders/:step',
            templateUrl: 'parent.html',
            controller: function ($scope, $stateParams) {
                $scope.itemId = $stateParams.itemId;
                $scope.step = $stateParams.step;
            }
        })

        .state('step.item', {
            url: '/:itemId',
            templateUrl: 'child.html',
            controller: function ($scope, $stateParams) {
                $scope.itemId = $stateParams.itemId;
                $scope.step = $stateParams.step;
            }

        });
}).controller('SimpleController', function($scope, $state){
  $scope.items = [1,2,3,4,5,6];
  $scope.steps = ["first", "second"];
})

I see only two ways:

  1. add an service and inject it to both controllers
  2. access a parent scope from a child controller and pass a parameter

    But both cause me to add watchers at a parent scope. Maybe i can acplish it easier?
Share Improve this question edited Nov 20, 2015 at 20:10 Radim Köhler 124k48 gold badges242 silver badges340 bronze badges asked Mar 16, 2015 at 4:53 fastecfastec 9171 gold badge8 silver badges18 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 4

First, you need to recognize that a parent state's controller runs only once when you enter the subtree of its child states, so switching between its children would not re-run the controller.

This is to say that if you want the itemId parameter to always be up to date, you'd need a $watch (which you tried to avoid).

For the first time, you could collect the itemId like so in the parent's state:

$scope.itemId = $state.params.itemId;

If you need it to be kept up-to-date, then you'd need to watch for changes, for example:

$scope.$watch(function(){
  return $state.params;
}, function(p){
  $scope.itemId = p.itemId;
});

plunker

Similarly, if you only need to place it in the view, then set $state on the scope:

$scope.$state = $state;

and in the view:

<div>parent /orders/{{step}}/{{$state.params.itemId}}</div>

EDIT:

I guess another way would be to call a scope-exposed function on the parent to update the value. I'm not a fan of such an approach, since it relies on scope inheritance (which is not always the same as state inheritance) and scope inheritance in a large application is difficult to track. But it removes the need for a $watch:

In the parent controller:

$scope.updateItemId = function(itemId){
   $scope.itemId = itemId;
};

and in the child controller:

if ($scope.updateItemId) $scope.updateItemId($stateParams.itemId)

I can see, that you've already found your answer, but I would like to show you different approach. And I would even name it as "the UI-Router built in approach".

It has been shown in the UI-Router example application, where if we go to child state with some ID = 42 like here we can also change the other, than the main view (the hint view in this case).

There is a working example with your scenario, showing that all in action.

What we do use, is the Multiple Named Views

The parent state, now defines root view, which is injected into unnamed ui-view="" inside of the root/index.html.

A new parent.html

<div ui-view="title"></div>
<div ui-view=""></div>

As we can see, it also contains another view target (than unnamed) - e.g. ui-view="title" which we fill with another template immediately... in parent state:

$stateProvider.state('step', {
  url: '/orders/:step',
  views : {
    '': {
      templateUrl: 'parent.html',
    },
    'title@step': {
      templateUrl:'parent_title_view.html',
    }
  }
})

And child? It can continue to handle main area... but also can change the title area. Both views are independent, and belong to child.

.state('step.item', {
  url: '/:itemId',
  views : {
    '': {
      templateUrl: 'child.html',
    },
    'title': {
      templateUrl:'child_title_view.html',
    }
  }

So, there are no watchers, nothing... which is not shipped with the UI-Router by default. We just adjust many places (views) with the child implementation. We can still provide their content with some defaults in parent..

Check that example here. Hope it will help to see it a bit differently...

I'm not sure if this will really solve your problem but you can do

   $stateProvider.state('step', {
            url: '/orders/:step/:itemId',
            templateUrl: 'parent.html',

http://plnkr.co/edit/sYCino1V0NOw3qlEALNj?p=preview

This means the parent state will collect the itemID on the way through.

include $state in resolve function for parent state

.state('parent', {
                url: '/parent',
                controller: 'parentController',
                resolve: {
                params: ['$state', function ($state) {
                            return  $state.params;
                        }]
                }
            })

then in parent controller inject the resloved vars

app.controller('parentController', ['$scope', 'params',
function ($scope, params) {

  $scope.params = params; 

}]);

in this way params available in both child and parent.

本文标签: javascriptHow to access parameter from nested state in parent stateStack Overflow