0

I have this plunker code.

What I'm trying to do, is to display the gray box one time per row.

To achieve this, I thought to modify the partition filter in order to return a JSON to add it a new property by row to know if the gray box is expanded or not.

But, I could Not successfully return a JSON.

Do you know how to modify the filter to return a JSON or a better way to show the gray box by row?

Related questions:

Update 1

The issue could be easily resolved by using the correct scope for the ng-repeat for the row without modifying the filter, thanks to @m59.

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

Update 2

Live Demo

If I try to modify the item, it seems the ng-repeat would be called again losing the props values.

  <div ng-repeat="friendRow in friends | partition:2" 
       ng-init="props = {}">
    <div ng-repeat="item in friendRow" 
         ng-click="collapse(item)" 
         ng-class="{myArrow: showArrow}">
      {{item.name}} {{item.age}} years old.
      <div>{{item.name}}</div>
    </div>
    <div collapse="!props.isExpanded">
      some content
      <br/>
      <input type="text" ng-model="currentItem.name">
    </div>
  </div>

js

  $scope.collapse = function(item){
     this.props.isExpanded  = !this.props.isExpanded;
     this.showArrow = !this.showArrow;
     $scope.currentItem = item;
  };

This causes the gray box to collapse each time the item is modified. Any clue?

Community
  • 1
  • 1
Coyolero
  • 2,353
  • 4
  • 25
  • 34
  • I recognize that code =D First, I really suggest using my updated code which is MUCH cleaner. – m59 Oct 04 '14 at 04:23
  • Great! Which code is that? – Coyolero Oct 04 '14 at 04:27
  • 1
    My apologies! I assumed you got the code from here: http://stackoverflow.com/questions/21644493/how-to-split-the-ng-repeat-data-with-three-columns-using-bootstrap/21653981#21653981 The improved code is at the bottom, with a link to GitHub for its dependencies. I'll post an answer to your question shortly. – m59 Oct 04 '14 at 04:29

1 Answers1

3

I've updated my code/answer regarding partitioning data. It's important to fully understand all of that before deciding on an approach to your project.

The problem you have in your plnkr demo is that you're modifying the parent $scope and not the scope of the ng-repeat for that row.

Just set a flag on the row and toggle it when clicked:

Live Demo

<div 
  class="row" 
  ng-repeat="friendRow in friends | partition:2"
  ng-init="isExpanded = false" 
  ng-click="isExpanded = !isExpanded"
>
  <div ng-repeat="item in friendRow">
    {{item.name}} {{item.age}} years old.  
  </div>
  <div collapse="!isExpanded">
    some content
  </div>
</div>

To access the correct scope within a function in the controller, you can use the this keyword instead of $scope. this will refer to the scope the function is called from, whereas $scope refers to the scope attached to the element with ng-controller (a parent of the ng-repeat scopes you want to target).

<div 
  class="row" 
  ng-repeat="friendRow in friends | partition:2" 
  ng-click="collapse()"
>

JS:

$scope.collapse = function() {
   this.isExpanded  = !this.isExpanded;
};

If you want to keep the ng-click directive on the item element instead of putting it on the row element as I have done, then you're dealing with another child scope because of that inner ng-repeat. Therefore, you will need to follow the "dot" rule so that the child scope can update the parent scope where the collapse directive is. This means you need to nest isExpanded in an object. In this example, I use ng-init="props = {}", and then use props.isExpanded. The dot rule works because the children share the same object reference to props, so the properties are shared rather than just copied, just like in normal JavaScript object references.

Live Demo

<div 
  class="row"
  ng-repeat="friendRow in friends | partition:2" 
  ng-init="props = {}"
>
  <div ng-repeat="item in friendRow" ng-click="collapse()">
    {{item.name}} {{item.age}} years old.
  </div>
  <div collapse="!props.isExpanded">
    some content
  </div>
</div>

JS:

$scope.collapse = function(){
   this.props.isExpanded  = !this.props.isExpanded;
};

Update

We keep going through more and more issues with your project. You really just need to experiment/research and understand everything that's going on on a deeper level, or it will just be one question after another. I'll give it one last effort to get you on the right track, but you need to try in the basic concepts and go from there.

You could get past the issue of props reinitializing by putting $scope.expandedStates and then passing the $index of the current ng-repeat to your function (or just using it in the view) and setting a property of expandedStates like $scope.expandedStates[$index] = !$scope.expandedStates[$index]. With the nested ng-repeat as it is, you'll need to do $parent.$index so that you're associating the state with the row rather than the item.

However, you'll then have another problem with the filter: Using my old partition code, the inputs inside the partitions are going to lose focus every time you type a character. Using the new code, the view updates, but the underlying model will not. You could use the partition filter from this answer to solve this, but from my understanding of that code, it could have some unexpected behavior down the road and it also requires passing in this as an argument to the filter. I don't recommend you do this.

Filters are meant to be idempotent, so stabilizing them via some kind of memoization is technically a hack. Some argue you should never do this at all, but I think it's fine. However, you definitely should ONLY do this when it is for display purposes and not for user input! Because you are accepting user input within the partitioned view, I suggest partitioning the data in the controller, then joining it back together either with a watch (continuous) or when you need to submit it.

$scope.partitionedFriends = partitionFilter($scope.friends, 2);

$scope.$watch('partitionedFriends', function(val) {
  $scope.friends = [].concat.apply([], val);
}, true); // deep watch
Community
  • 1
  • 1
m59
  • 43,214
  • 14
  • 119
  • 136
  • Very useful answer! Thanks. I try to use a function to update the `isExpanded` variable but is not being reflected in the view, do you know what I doing wrong? [link](http://plnkr.co/edit/HAiikjNDaI6ifRlPjxDs?p=preview) – Coyolero Oct 04 '14 at 05:24
  • 1
    @Coyolero yes, you have the `ng-click` on the wrong element. See my answer. It belongs on the row element, but you have it on the child `item` element, which is another child scope. You need to use the "dot" rule in order for this to work. I updated my answer to demonstrate all of this. – m59 Oct 04 '14 at 12:38
  • @Coyolero is there anything else you need to know about this? – m59 Oct 05 '14 at 00:31
  • I have another problem with this solution. If I try to modify the `item` inside the `collapse()`, the gray box would not be shown http://plnkr.co/edit/PRq5sYeYBgcLcAzIJDOw?p=preview – Coyolero Oct 05 '14 at 17:36
  • 1
    @Coyolero that is strange, but you shouldn't be modifying the data like that anyway. I think the problem is that you're causing the `ng-repeat`s to render again when you make that change and that probably throws things off. Never change your data just to do something in the view. Instead, you should just do this: `ng-class="{myArrow: showArrow}"` and `this.showArrow = !this.showArrow;` http://plnkr.co/edit/3se3qKU3iQN4wZFbDa5a?p=preview – m59 Oct 05 '14 at 17:47
  • hmm that makes sense, if the `ng-repeat` is render again the changes in the `props` are lost. But what if I want to modify something in the model like this: http://plnkr.co/edit/XuAWNtTZgFE2dwTcuuJC?p=preview, the `ng-repeat` would be render again and the changes would be lost. There is a way to have more control with the props variable? – Coyolero Oct 05 '14 at 18:06
  • @Coyolero you're really putting me to work :) See my updated answer. – m59 Oct 05 '14 at 20:18
  • Guys i need an help starting from your examples. This is a new plnkr i created: http://plnkr.co/edit/a2hGmuaATI0BDkSaqNPA?p=preview in which i changed the width of expanded element from 100% to 33%. What i need is that when i expand the element on the left, for example, the column of the right side should stay exactly in the same position. Actually, instead, it creates a blank space! Any idea how solve this issue? Thanks – Atlas91 Oct 13 '16 at 08:59