Updating values inside an ng-repeat - angularjs

I'm trying to use AngularJS's ng-repeat to check if a value from a previous iteration is the same as the current.
if previous === current
After, the check I would like to update previous to equal current.
previous = current
I have no idea how to update values from inside an ng-repeat in Angular.
Here is an example:
<div ng-repeat="product in Products | filter: filters[0] | orderBy:'Section'">
<div ng-if="product.Section !== $parent.previous">
{{product.Section}}
</div>
$parent.previous = product.Section
</div>
Update:
While this question might still be helpful in some situations, after some thought I realised that what I really want is new objects grouping products by section: How can I group data with an Angular filter?

Related

Get original object name from firebase object inside ng-repeat

I get from firebase an object with the following code:
var ref = firebase.database().ref().child("categories");
ref.orderByChild("status")
.equalTo('featured')
.on('value', function(snap) {
console.log('Categories in range', snap.val());
$scope.categories = snap.val();
})
The object I get contains a couple of objects with the data which look like my example here:
Example Object from firebase
The results i use in my template view with ng-repeat.
<ul class="list">
<li ng-repeat="(category_id, category) in categories | toArray | orderBy:'position'">
<div style="background-image: url({{category.images[0].src}});" ng-click="category_action(category_id)">{{category.name}}
</div>
</li>
My problem is now that I want to give the original object name back with ng-click. I tried to use the (key, value) solution from angular for ng-repeat but I dont get the original object name back and only a index number like 0,1,2,3 etc.
What I need is the original name like in this example KU8JfAZRCsPJy9uOMqm.
If i only get the index number (0,1,2) back how can I know in my controller which object name this was originaly? Like for example $scope.categories.???
Thank you for your help in advance, I did research but found only people with the same problem but no working solution.
PS: I use firebase 3.x and angular 1.x

Display size of ng-repeat with filter

I am currently working on an analytics backend web app and am using ng-repeat to show a table for some data I am working with. Is there a way to show the number of total results found when using a filter with ng-repeat? I would like to display this beneath my table.
You can retrieve the filtered data in a var like this :
<div ng-repeat="product in filteredProducts = (products | filter:search)">
So inside your ng-repeat you can do something like filteredProducts.length
You can use the $last variable of ngRepeat
something like this inside your ng-repeat would do the trick:
<div ng-if="$last"> Total results found: {{ $index + 1 }} </div>
note that you should add 1 to the $index since it starts at 0

NG-Repeat doesn't update cleanly when scope is updated

So I have a list of workspaces:
<li class="workspace-object" ng-repeat="w in workspaces | filter:searchQuery" ng-click="selectWorkspace(w)">
{{w.name | titleCase }}
</li>
That List gets updated in the database by some function, and then I call the loadWorkspaces function below, to re-populate the $scope.workspaces object
$scope.loadWorkspaces = function() {
APIService.getWorkspaces().then(function(data){
$scope.workspaces = data;
});
}
When this happens, the $scope.workspaces, when logged out, reads the right updated information immediately, however, the ng-repeat DUPLICATES, before it updates to the proper list. I have no idea why this happens. Any ideas?
In this example, I am updating the workspace title, and that update runs the loadworkspaces function.
Try this ...
w in workspaces track by $index
When the contents of the collection change, ngRepeat makes the corresponding > changes to the DOM:
When an item is added, a new instance of the template is added to the DOM.
When an item is removed, its template instance is removed from the DOM.
When items are reordered, their respective templates are reordered in the DOM.
By default, ngRepeat does not allow duplicate items in arrays. This is because when there are duplicates, it is not possible to maintain a one-to-one mapping between collection items and DOM elements.
If you do need to repeat duplicate items, you can substitute the default tracking behavior with your own using the track by expression.
For example, you may track items by the index of each item in the collection, using the special scope property $index
Accepted answer did not help me (Using Angular 1.1.10), my repeat was still updating delayed. I struggled for ages before I got it working.
Instead of repeating my custom directive like so;
<my-directive ng-repeat="item in myarray track by item.id" some-binding="item"></my-directive>
I (magically) got it working after moving the ng-repeat to a (non-directive) parent element, like so:
<div ng-repeat="item in myarray track by item.id">
<my-directive some-binding="item"></my-directive>
</div>

Reordering ng-repeat after editing an element

Suppose I have an row ng-repeat with orderBy at a certain property/column say name. If I edit a column, it redirects into another page with the fields as editable input. When I edit name and commit the edit, it goes back to the table page, but the edited element stays at the previous position when it should have moved somewhere. So how do I make AngularJS compute orderBy again? Thanks.
You're doing something wrong. Here's a working demo (click on a person to edit their age).
And here's some code needed to post my answer with a link to codepen:
<div ng-repeat="person in people | orderBy:'age'">...</div>
In your view you can do it this way:
<div ng-repeat="item in sortedItems = (allItems | orderBy : itemProperty : reverse)" >
</div>
If you want to to do it from your controller( you have to remove filter from the example above):
$scope.$watch('sortedItems', function() {
$scope.allItems = orderBy($scope.allItems, itemProperty, reverse);
}, true);
The second option will do the job for you, but is definitely not a good practice, and you should avoid it if possible.

How to set a boolean flag to collapse/expand a row with ng-repeat

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:
Push down a series of divs when another div is shown
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?
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

Resources