Search code examples
angularjsangularjs-ng-repeatangular-filters

AngularJS: GroupBy and show items


I've got an application where I retreive the information of some objects (into an array). That array would have the following structure:

$scope.items = [    
    {
        id: 23289323,
        event: {
            id: 972823,
            name: 'Event A name',
            datetime: '2017-02-01 13:45',
        },
        player: {
            id: 58392,
            name: 'Player A name'
        },
        team: {
            id: 38839,
            name: 'Team A'
        },
        datetime: '2017-02-03 22:23'
    },
    {
        id: 482273784,
        event: {
            id: 972823,
            name: 'Event A name',
            datetime: '2017-02-01 13:45',
        },
        player: {
            id: 2989273,
            name: 'Player B name'
        },
        team: {
            id: 2323434,
            name: 'Team B'
        },
        datetime: '2017-02-03 22:23'
    },
    {
        id: 283273939,
        event: {
            id: 23092803,
            name: 'Event B name',
            datetime: '2017-02-01 13:45',
        },
        player: {
            id: 58392,
            name: 'Player A name'
        },
        team: {
            id: 38839,
            name: 'Team A'
        },
        datetime: '2017-02-03 22:23'
    }
    ...
]

What I'd like

I'd like to be able to have two lists.

On the left, a list of some customizable groupingBy AngularJS filter. So, I can specify "group it by player" and it shows, on this left list, a list of the players with some information (for example, showing the player's name).

On the right, when I select a specific player, show the items that have this player associated.

What I've tried

<li data-ng-repeat="(key, value) in Ctrl.items | groupBy: 'event.id'">
{{key}}<br/>{{value}}
</li>

What I get

23289323
{id: 23289323,event: {id: 972823,name: 'Event name',datetime: '2017-02-01 13:45',}, player: {id: 58392, name: 'Player name'}, team: { id: 38839,name: 'Team A'}, datetime: '2017-02-03 22:23'}

So, I'm getting the whole item object, but I've not found any way of getting the item that I'm groupBying. Because, right now, if there are 3 items with that event.id I get three <li></li> in stead of only one (the one of the event object).

What I ask

Is there any way of using AngularJS groupBy filter and getting in return the (whole) object that is specifying the grouping?

Remember that the groupBy key can be changed by the user.

If you need any further information, please let me know.

Thank you!


Solution

  • I think I've made it through a custom filter. I'm not sure if it's the best way, so if anyone has another solution, please post it!

    This is the code of the filter:

    (function(){
        angular.module("AppModule").filter('groupByGrouped', function() {
            return function(list, groupedElement) {
                var result = [];
                var used_elements = [];
                var ref_item;
                var ref_check;
    
                // Loop through every list item
                angular.forEach(list, function(item){
                    // We take the object we want to group by inside the item 
                    ref_item = item[groupedElement];
    
                    // If it exists
                    if(ref_item !== undefined){
                        if(ref_item.id !== undefined){
                            // If it has an ID, we take the ID to make it faster. 
                            ref_check = ref_item.id;
                        }else{
                            // Otherwise, we identify the specific object by JSON string (slower method)
                            ref_check = JSON.stringify(ref_item);
                        } 
    
                        // If it does not exist yet
                        if(used_elements.indexOf(ref_check) == -1){
                            // We add it to the results
                            result.push(ref_item);
                            // And we add it to the already used elements so we don't add it twice
                            used_elements.push(ref_check);
                        }
                    }else{
                        // Otherwise we log it into our console
                        console.warn("The key '"+groupedElement+"' inside the specified item in this list, does not exist.");
                    }
                });
    
                return result;
            };
        });
    })();
    

    This will return the whole object. So our HTML would be something like:

    <ul>
        <li data-ng-repeat="(key, obj) in Ctrl.items | groupByGrouped: 'event'">
            <span class="object_id">{{obj.id}}</span>
        </li>
    </ul>
    

    Or even with a directive (not tested, but should work aswell):

    <ul>
        <li data-ng-repeat="(key, obj) in Ctrl.items | groupByGrouped: 'event'">
            <obj_directive ourobject="obj"></obj_directive>
        </li>
    </ul>