Steve
Steve

Reputation: 237

AngularJS: Setting a variable in a ng-repeat generated scope

How do I access the set of scopes generated by an ng-repeat?

I suppose at the heart of this is the fact that I don't understand quite how the relationship works between a) the collection of objects that I pass into the ng-repeat directive and b) the collection of scopes that it generates. I can play around with (a), which the ng-repeat scope watches and picks up, but how do I set variables on the scope itself (b)?

My use case is that I have a set of elements repeating using ng-repeat, each of which has an edit view that gets toggled using ng-show/ng-hide; the state for each element is held in a variable in the local scope. I want to be able to trigger an ng-show on a particular element, but I want the trigger to be called from outside the ng-repeat, so I need to be able to access the local scope variable.

Can anyone point me in the right direction (or tell me if I'm barking up the wrong tree)?

Thanks

Update: Link below was very helpful thankful. In the end I created a directive for each of the repeating elements, and used the directive's link function to add its scope to a collection on the root scope.

Upvotes: 17

Views: 36802

Answers (3)

Spencer Judd
Spencer Judd

Reputation: 409

Ben Nadel has given a pretty clean solution to the "how do I assign to an ngRepeat's $scope" problem, which I just implemented in my own project. Essentially, you can add an ngController directive alongside your ngRepeat, and manipulate ngRepeat's $scope inside the controller.

Below is my own contrived example, which demonstrates assigning to ngRepeat's $scope in a controller. Yes, there are better ways to do this exact thing. See Ben Nadel's post for a better example.

<div ng-controller="ListCtrl">
  <h1>ngRepeat + ngController</h1>
  <ul>
    <li ng-repeat="item in items" ng-controller="ItemCtrl" ng-show="isVisible">
      {{item.name}}
      <button ng-click="hide()">hide me!</button>
    </li>
  </ul>
</div>

<script type="text/javascript">
  var app = angular.module("myApp", []);

  app.controller("ListCtrl", function($scope) {
    $scope.items = [
      {name: "Item 1"},
      {name: "Item 2"},
      {name: "Item 3"}
    ];
  });

  app.controller("ItemCtrl", function($scope) {
    $scope.isVisible = true;
    $scope.hide = function() {
      $scope.isVisible = false;
    };
  });
</script>

EDIT: Having re-read your question, seeing that you need to manipulate a bunch of child scopes in a parent scope, I think that your directive(s) approach is the way to go. I still think this answer may be useful to some, as I came across your question while looking for this answer.

Upvotes: 11

Michael Bromley
Michael Bromley

Reputation: 4822

Here is a pretty simple way to do what I think you are trying to do (I figured this out when I needed to do something similar):

We know that each repeated item has it's own scope created for it. If we could pass this scope to a method defined on the parent scope, then we'd be able to do what we want with it in terms of manipulating or adding properties. It turn out this can be done by passing this as an argument:

Example

// collection on controller scope
$scope.myCollection = [
  { name: 'John', age: 25 },
  { name: 'Barry', age: 43 },
  { name: 'Kim', age: 26 },
  { name: 'Susan', age: 51 },
  { name: 'Fritz', age: 19 }
];



// template view
<ul>
  <li ng-repeat="person in myCollection">
    <span ng-class="{ bold : isBold }">{{ person.name }} is aged {{ person.age }} </span>
    <button class="btn btn-default btn-xs" ng-click="toggleBold(this)">toggle bold</button>
  </li>
</ul>

So when we press the "toggle bold" button, we are calling the $scope.toggleBold() method that we need to define on the controller's $scope. Notice that we pass this as the argument, which is actually the current ng-repeat scope object.

Therefore we can manipulate it like this

$scope.toggleBold = function(repeatScope) {
  if (repeatScope.isBold) {
    repeatScope.isBold = false;
  } else {
    repeatScope.isBold = true;
  }
};

Here is a working example: http://plnkr.co/edit/Vg9ipoEP6wxG8M1kpiW3?p=preview

Upvotes: 19

Joe Minichino
Joe Minichino

Reputation: 2773

When working within a hierarchy of scopes I find very useful to dispatch events with $emit and $broadcast. $emit dispatches an event upwards so your child scopes can notify parent scopes of a particular event. $broadcast is the other way round.

Alternatively, as child scopes have access to parent scope properties you could trigger changes by using $watch on a particular property in the parent scope.

UPDATE: As for accessing the child scopes, this may turn useful for you : Get to get all child scopes in Angularjs given the parent scope

Upvotes: 1

Related Questions