9

I am passing in 2 arrays to my view. I would like my nested loop to only display where it's parent_id value matches the parent.id. Eg.

arr1 = {"0":{"id":326,"parent_id":0,"title":"Mellow Mushroom voucher","full_name":"Patrick","message":"The voucher says $10 Voucher; some wording on the printout says, \"This voucher is valid for $20 Pizza\" but my purchase price or amount paid also says $20. Shouldn't that be $10","type":"Deals"}};
arr2 = {"0":{"id":327,"parent_id":326,"title":"Re: Mellow Mushroom voucher","full_name":"Patrick Williams","message":"Some message here","type":null};

...
<div data-ng-repeat = "parent in arr1">
<span>{{parent.title}}<span>
    <div data-ng-repeat="child in arr2 | only-show-where-child.parent_id == parent.id">
        <li>{{child.body}}</li>
    </div>
</div>

Is this possible/best practice in angular of should I be filtering the object in node before passing it into angular? Thank you!

davet
  • 117
  • 1
  • 1
  • 6
  • I'm assuming arr1 and arr2 are really arrays and not just objects, if so then can you edit your code? I would myself but I don't want to if that is actually part of the problem. – Jason Goemaat Nov 25 '14 at 21:36
  • They ARE objects, sorry! Made edits to each object. – davet Nov 25 '14 at 22:00

3 Answers3

11

There are a couple of ways you could do it... You could create a function to return just the children:

$scope.getChildren = function(parent) {
  var children = [];
  for (var i = 0; i < arr2.length; i++) {
    if (arr2[i].parent_id == parent.id) {
      children.push(arr2[i]);
    }
  }
  return children;
};

html:

<div ng-repeat="child in getChildren(parent)">

You could define a filter to do the same thing:

myApp.filter('children', function() {
  return function(input, parent) {
    var children = [];
    for (var i = 0; i < input.length; i++) {
      if (input[i].parent_id == parent.id) {
        children.push(input[i]);
      }
    }
    return children;
  };
});

html:

<div ng-repeat="child in arr2|children:parent">

Both of those methods will execute every digest cycle though. If you have a large list of elements you would definitely want to improve performance. I think the best way would be to pre-process those results when you get them, adding a children array to each object in arr1 with only its children (here using array.filter instead of for loop and array.forEach):

arr1.forEach(function(parent) {
  parent.children = arr2.filter(function(value) {
    return value.parent_id === parent.id;
  };
});

Then in the html you are already working with the parent so you can repeat over its children property:

<div ng-repeat="child in parent.children">
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
10

Instead of using filters, data-ng-if can achieve the same result.

<div data-ng-repeat="parent in arr1">
  <span>{{parent.title}}<span>
  <div data-ng-repeat="child in arr2" data-ng-if="child.parent_id == parent.id">
    <li>{{child.body}}</li>
  </div>
</div>
AncientSwordRage
  • 7,086
  • 19
  • 90
  • 173
Riad Baghbanli
  • 3,105
  • 1
  • 12
  • 20
0

The solution depends on how often arrays are changed and how big arrays are.

The fist solution is to use filter. But in this case it would be called at least twice (to make sure that result is "stabilized" - selected same elements).

Other solution is to $watch by yourself original array and prepare "view" version of it injecting children there. Personally I would prefer the second as more explicit.

However if you can reuse "find-the0child" filter in other parts of your application you can go with first one - AngularJS will re-run filter only after original array modified.

If needed I can provide here an example of implementation of one of these options - add the comment to answer.

Valentyn Shybanov
  • 19,331
  • 7
  • 66
  • 59