AngularJS "headers" in ng-repeat

I have a problem that has arisen in more cases than recently, and I wonder how best to do this. To say it simply:

I have data displayed in ng repeat sorted by specific element. Say this is sorted by name, for example. My goal consists of headings on the gap of letters in the alphabetical list:

----A---- Abe Lincoln Adam Smith ----B---- Barack Obama Barry Zuckercorn ----C---- ... 

etc.

Things I've tried include:

  • After the controller completely rebuilds the model data when it enters, manually placing it in an array of letter groups. For example, my service has an array of “messages”, and my controller, like service updates, manually shuffles these “messages” into an array of “letter groups”. Then it’s possible just to have two ng repeats nested

However, this depends on heavy data manipulation, and it is not easy to change the fact that data is sorted on the fly.

  • When the model is updated, the controller “dops” the data, manually stepping over it, checking when the letter changes, and overwriting the “title” property for this element ( post.header = true ). Then ng-repeat can check if the element that it is currently the header is and use ng-if to insert something else into the DOM.

This looks a little cleaner, but because of the way ng-repeat works, the title element should be “included” at the same level as the ng-repeat element. For example, if you execute ng-repeat elements on <tr> , this means that it would be impossible to insert another <tr> for the header, since a special element must happen inside <tr>

And finally, along the same lines as above:

  • Whether the controller supports the list of "key indexes" - this has the advantage of not modifying the data, as the second method above, but it works in general the same way.

EDIT:

I wrote a blog post here explaining how I ended up dealing with this issue - thanks to the discussion of Google groups related to Ian below.

+6
source share
2 answers

Create an Angular filter. It can take a sorted list, cut it by the first letter and return an object for each fragment with a letter and an array of objects starting with this letter.

See for example chunk by size filter here . Pay particular attention to the discussion of creating hash values ​​for your highlighted values.

+5
source

I wrote a small directive that monitors objects and automatically adds the $ header property to the first in the list. Use it like this:

  <div ng-repeat="item in data.items | orderBy:... | filter:... as filteredItems" header-list objects="filteredItems" header="getHeader"> <div ng-show="item.$header">{{item.$header}}</div> <div>Your normal content</div> </div> 

You can pass a function that builds the title as you wish. A function can be an instance method or a function in your scope. To get the first letter, define in your controller.

 $scope.getHeader = function(item) { return item.name.slice(0, 1); }, 

The list will be automatically updated when filters and orders are changed.

 app.module("...").directive('headerList', function () { return { restrict: 'A', scope: { objects: '=', header: '=' // This must be a function or instance method }, link: function(scope) { scope.$watch('objects', function() { var lastHeader, currentHeader; scope.objects.forEach(function (obj) { // We pass obj twice, so it can be used is instance method or regular function that takes the object as argument. currentHeader = scope.header.call(obj, obj); // in order to display a header per type, we mark the first event of each kind as header if (currentHeader !== lastHeader) { obj.$header = currentHeader; lastHeader = currentHeader; } else { obj.$header = null; } }); }); } } }); 
+1
source

Source: https://habr.com/ru/post/948349/


All Articles