最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Named view in angular ui-router not updating, despite being watched - Stack Overflow

programmeradmin4浏览0评论

I have a scoped variable $scope.foo that I am keeping a watch on. It could be updated through a text field in a form.

I have two named views A and B on a page that I am rendering using angular ui-router. The named view A has the text form field that is being watched for changes in a controller through ng-model="foo". When the value of foo is changed by a user it changes the value of another scoped variable $scope.bar, which is an array, in the controller that is being used in the ng-repeat directive on the named view B. The changes in the $scope.bar is made using $scope.$watch method in the controller.

The issue that I am facing is that the when the foo is changed I could see the changes in bar on the named view A but not on the named view B.

Could somebody help me resolve this issue?

Edit: Here is the plunker for this issue.

I have a scoped variable $scope.foo that I am keeping a watch on. It could be updated through a text field in a form.

I have two named views A and B on a page that I am rendering using angular ui-router. The named view A has the text form field that is being watched for changes in a controller through ng-model="foo". When the value of foo is changed by a user it changes the value of another scoped variable $scope.bar, which is an array, in the controller that is being used in the ng-repeat directive on the named view B. The changes in the $scope.bar is made using $scope.$watch method in the controller.

The issue that I am facing is that the when the foo is changed I could see the changes in bar on the named view A but not on the named view B.

Could somebody help me resolve this issue?

Edit: Here is the plunker for this issue.

Share Improve this question edited Aug 21, 2014 at 7:08 skip asked Aug 21, 2014 at 4:45 skipskip 12.7k34 gold badges117 silver badges159 bronze badges 2
  • Can you just post a test fiddle for the same?? – Manish Kr. Shukla Commented Aug 21, 2014 at 5:00
  • @TechMa9iac: Hello Tech here plnkr.co/edit/y7BMdyRhj2WvFXq0UUud?p=preview is the plunker of the issue I have been facing. – skip Commented Aug 21, 2014 at 7:00
Add a ment  | 

2 Answers 2

Reset to default 5

There is a plunker, which should show that your scenario is working.

The most important part of that solution is driven by:

  • Scope Inheritance by View Hierarchy Only (cite:)

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.

Let me express it again: A scope inheritance goes only via the view nesting.

With that we can create this states definitions:

$stateProvider
    .state('root', { 
        url: '/root',
        templateUrl: 'tpl.root.html',
        controller: 'RootCtrl',            // this root scope will be parent
    })
    .state('root.entity', {
        url: '/entity',
        views:{
            'A': {
                templateUrl: 'tpl.a.html',
                controller: 'ACtrl',        // scope is inherited from Root
            },
            'B': {
                templateUrl: 'tpl.b.html',
                controller: 'ACtrl',        // scope is inherited from Root
            }
        }
    })

So the state defintion support nested views - let's profit from that and place the $scope.bar collection into the parent. All views involved will then have access to the same collection:

.controller('RootCtrl', function ($scope, $state) {
  $scope.bar = ['first', 'second', 'last'];

})
.controller('ACtrl', function ($scope, $state) {
  // *) note below
  $scope.foo = $scope.bar[0];
  $scope.$watch("foo", function(val){$scope.bar[0] = val; });
})
.controller('BCtrl', function ($scope, $state) {

})

*) note: here we do 1) set from bar 2) $watch and 3) set back to bar to follow the question description... but if the array would contain objects, we can work with them directly... without that overhead, but that's another story...

Check here how that works, and that any changes in view A are also visible in B ... because of inherited reference to the array bar declared in parent $scope.

I created the second answer, to follow also the issue in this plunker, which @skip (OP) passed me as the example fo the issue.

Firstly There is an updated working version

of that plunker, which does what we need. There are the main changes:

The original state def:

.state('home', {
            url: '/',
            views: {

                '': { templateUrl: 'home.html' },

                'A@home': {
                    templateUrl: 'a.html',
                    controller: 'MainCtrl'
                },

                'B@home': {
                    templateUrl: 'b.html',
                    controller: 'MainCtrl'
                }
            }

Was replaced with the RootCtrl defintion:

.state('home', {
            url: '/',
            views: {

                '': { 
                  templateUrl: 'home.html', 
                  controller: 'RootCtrl' // here we do use parent scoping
                },

                'A@home': {
                    templateUrl: 'a.html',
                    controller: 'MainCtrl'
                },

                'B@home': {
                    templateUrl: 'b.html',
                    controller: 'MainCtrl'
                }
            }

And this was one controller:

app.controller('MainCtrl', function($scope) {
  var fruits = [{"name": "Apple"}, {"name": "Banana"}, {"name": "Carrot"}];

  $scope.bar =  $scope.bar || []; 

  $scope.foo = 2;

  $scope.$watch('foo',function(value, oldValue){
      $scope.bar = [];
      getBar(fruits, value);
  });

  function getBar(fruits, howManyFruits) {
    for(var i=0; i < $scope.foo; i++) {
        $scope.bar.push(fruits[i]);
    }
  }

});

But now we do have two (Parent and child):

app.controller('RootCtrl', function($scope) { 
  $scope.bar = []; 
})
app.controller('MainCtrl', function($scope) {
  var fruits = [{"name": "Apple"}, {"name": "Banana"}, {"name": "Carrot"}];

  //$scope.bar =  $scope.bar || []; 

  $scope.foo = 2;

  $scope.$watch('foo',function(value, oldValue){
      $scope.bar.length = 0;
      getBar(fruits, value);
  });

  function getBar(fruits, howManyFruits) {
    for(var i=0; i < $scope.foo; i++) {
        $scope.bar.push(fruits[i]);
    }
  }

});

Some important parts to mention

I. The least mon denominator

We have to move the shared collection (array bar) into the parent. Why?

we have to move the shared reference to the least mon denominator - to the parent scope

see

  • How do I prevent reload on named view, when state changes? AngularJS UI-Router

II. The Reference to array must be unchanged

we have to keep the reference to the Parent $scope.bar unchanged!. This is essential. How to achieve that? see:

  • Short way to replace content of an array

where instead of creating new reference, we clear the array, keeping the reference to it

// wrong
$scope.bar = [];
// good
$scope.bar.length = 0;

III. Controller can have multiple instances

Also, the fact that both views A and B had the same controller (same controller name in fact), definitely did not mean, that they were the same instance.

No, they were two different instances... not sharing anything. That is I guess, the most critical confusion. see

  • angularjs guide - Dependency Injection

Controllers are special in that, unlike services, there can be many instances of them in the application. For example, there would be one instance for every ng-controller directive in the template.

Please, observe that all in the updated example

发布评论

评论列表(0)

  1. 暂无评论