I'm looking way to do something like
JS
$scope.players = [
{name: 'Gene', team: 'alpha'},
{name: 'George', team: 'beta'},
{name: 'Steve', team: 'gamma'},
{name: 'Paula', team: 'beta'},
{name: 'Scruath', team: 'gamma'}
];
HTML:
<ul repeat.for="obj of players | groupBy: 'team'">
Group name: ${obj.group}
<li repeat.for="player of obj.values">
player: ${player.name}
</li>
</ul>
Is it possible to do? Or what the better way to do this logic in Aurelia way?
Click Menu, and then select Create Group Filter. Select the Group Filter Type: Expression or PL/SQL. For PL/SQL filters, ensure that you specify the PL/SQL Package as the Oracle DB Default Package in the data model properties. To enter an expression, select the elements and move the elements to the Group Filter definition box.
To ensure that Aurelia can observe the changes on your array, make use of the Array methods: Array.prototype.push, Array.prototype.pop and Array.prototype.splice. Two-way binding with arrays requires a special syntax due to the nature of the for-of loop in javascript.
This means that Aurelia doesn't slow down as you add more complex functionality to your template and view-model. Binding in Aurelia allows data from the view-model to drive template behavior.
But don't worry, there is no dirty-checking. Aurelia uses an observable-based binding system that reacts to changes as they happen without having to do dirty-checking. This means that Aurelia doesn't slow down as you add more complex functionality to your template and view-model.
You can do this using a ValueConverter
.
export class GroupByValueConverter {
toView(array, groupBy) {
var groups = {};
array.forEach(function (o) {
var group = o[groupBy];
groups[group] = groups[group] || [];
groups[group].push(o);
});
return Object.keys(groups).map(function (group) {
return {
group: group,
values: groups[group]
};
})
}
}
After finding this answer, I did it in a slightly different way. Instead of using a an array of objects with group
and value
keys, I used a Map
.
Updated view
<ul repeat.for="[group, values] of players | groupBy:'team'">
Group name: ${group}
<li repeat.for="player of values">
player: ${player.name}
</li>
</ul>
For the value converter I used this answer for inspiration on an efficient way to perform a group by operation.
Value Converter
export class GroupByValueConverter {
toView(objects, key) {
return objects.reduce(
(rv, x) => rv.set(x[key], (rv.get(x[key]) || []).concat(x)),
new Map()
);
}
}
An extension to the ValueConverter above allowing to use a grouping filter with nested object properties (eg. groupBy:'team.id')
export class GroupByValueConverter {
toView(array, groupBy) {
var groups = {};
var props = groupBy.split(".");
array.forEach(function (o) {
var group = o;
props.forEach(function (p) { group = group[p] });
groups[group] = groups[group] || [];
groups[group].push(o);
});
return Object.keys(groups).map(function (group) {
return {
group: group,
values: groups[group],
};
})
}
}
Yet another extension that allows to specify as group an object. It takes a second parameter for specifying the object key to be used as indexer.
eg. - | groupBy:'team':'id' - | groupBy:'projectMember.team':'id'
export class GroupByValueConverter {
toView(array, groupBy, groupByKey) {
var groups = {};
var groupMembers = {};
var props = groupBy.split(".");
array.forEach(function (o) {
var group = o;
props.forEach(function (p) { group = group[p] });
var index = groupByKey && group ? group[groupByKey] : group;
groups[index] = group;
groupMembers[index] = groupMembers[index] || [];
groupMembers[index].push(o);
});
return Object.keys(groups).map(function (index) {
return {
group: groups[index],
values: groupMembers[index],
};
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With