David Hernandez · Mat Ryer · 1 Feb 2020
Mat Ryer · David Hernandez · 1 Feb 2020
Given a set of list data, it would be nice to represent this to the user in a grouped fashion, perhaps grouped by the when
field in the following example:
const data = [
{
"when": "Today",
"title": "This happened",
"author": {
"Name": "Mat",
"PhotoURL:": "mat.png",
}
},
{
"when": "Today",
"title": "And this happened",
"author": {
"Name": "David",
"PhotoURL:": "david.png",
}
},
{
"when": "Yesterday",
"title": "That happened",
"author": {
"Name": "David",
"PhotoURL:": "david.png",
}
},
]
We might like to see the data
represented visually like this:
Today
This happened
And this happened
Yesterday
That happened
In this post we will build a Grouper
component for Svelte that provides this functionality using slots.
Create a new Svelte component called Grouper.svelte
and add the following script and markup:
<script>
// items is an array of items to group.
export let items
// groupForItem is a function that takes an item,
// and returns which group it should be in.
export let groupForItem = function (item) {
return 'Group name'
}
let groupedItems = null
$: if (items) {
groupedItems = groupAll(items)
}
function groupAll(items) {
const groupedItems = []
let lastGroup = null
let group = null
items.forEach((item) => {
const itemGroup = groupForItem(item)
if (lastGroup == null || lastGroup != itemGroup) {
lastGroup = itemGroup
group = {
group: itemGroup,
items: []
}
groupedItems.push(group)
}
group.items.push(item)
})
return groupedItems
}
</script>
{#each groupedItems as groupedItem}
<slot
name='group'
group={groupedItem.group}
/>
{#each groupedItem.items as item}
<slot
name='item'
item={item}
/>
{/each}
{/each}
Using Svelte’s $ notation, we update groupedItems
whenever items
changes (including the first time it is set). The groupAll
function calls the groupForItem
grouper function for each item.
It is the groupForItem
grouper function that allows us to decide which group an item will go into.
We essentially build up the groupedItems
data so that it follows this structure:
[
{
group: "Today",
items: [
{
when: "Today",
title: "My blog title 4",
},
{
when: "Today",
title: "My blog title 3",
},
{
when: "Yesterday",
title: "My blog title 2",
},
// ...
]
},
{
group: "Yesterday",
items: [
{
when: "Yesterday",
title: "My blog title 1",
}
]
}
]
Now that we have the items
in a more suitable structure, we can think about rendering them.
Our Grouper
component uses Svelte slots (learn more about slots in the Svelte API docs) to allow users to provide the HTML template for groups and items. To see how it does this, pay attention to the {#each}
block in the previous code.
When rendered, this will show a group heading for each group, and then render each item underneath.
You are free to add other markup and props to make your Grouper more powerful, but for this post we’ll keep things as simple as possible.
An example usage of Grouper
is outlined here:
<Grouper
items={data}
groupForItem={ groupForItem }
let:group={group}
let:item={item}
>
<div slot='group'>
<!-- group header template -->
</div>
<div slot='item'>
<!-- item template -->
</div>
</Grouper>
items
- (required array) the list of items to groupgroupForItem
- (required function) a Group function that returns a group for the given itemlet:group={group}
- Gives the slots inside the Grouper
access to the grouplet:item={item}
- Gives the slots inside the Grouper
access to an item from the items
array<div slot='group'>
- The slot to use when rendering the group (this slot usually uses the {group}
field to decide what to show)<div slot='item'>
- The slot to use when rendering the item (this slot usually uses the {item}
field to decide what to show)The groupForItem
function is a simple callback that, given an item, returns the item’s group. This is how you decide what to group by.
For example, to group by the When
field in our data
array, we might write a groupForItem
like this:
function groupForItem(item) {
return item.when
}
<div slot='item'>
The item slot is how you render each item.
Every item will be rendered, but they’ll be nested inside a group (see next section).
An example item slot might just output the fields as you normally would inside an {each}
block:
<div slot='item'>
<a href='items/{item.id}'>
{item.name}
</a>
{if item.isNew}
<span class='highlight'>
NEW
</span>
{/if}
</div>
<div slot='group'>
The group slot is where you render the group headers. Usually you can just output the {group}
field directly like this:
<div slot='group'>
{group}
</div>
This works if the string that represents the group is the same as the one we will show to the user, like our When
field.
If we want to use more complex data, you will need to get one of the items from within the group slot. I’ll leave this an an exercise for the reader.
A lot of our blog posts come out of the technical work behind a project we're working on called Pace.
We were frustrated by communication and project management tools that interrupt your flow and overly complicated workflows turn simple tasks, hard. So we decided to build Pace.
Pace is a new minimalist project management tool for tech teams. We promote asynchronous communication by default, while allowing for those times when you really need to chat.
We shift the way work is assigned by allowing only self-assignment, creating a more empowered team and protecting the attention and focus of devs.
We're currently live and would love you to try it and share your opinions on what project management tools should and shouldn't do.
What next? Start your 14 day free trial to see if Pace is right for your team
or you can share the URL directly:
https://pace.dev/blog/2020/02/01/grouper-component-for-svelte-by-mat-ryer.html
Thank you, we don't do ads so we rely on you to spread the word.
If you have any questions about our tech stack, working practices, or how project management will work in Pace, tweet us any time. :)
— pace.dev (@pacedotdev) February 25, 2020
Passive user preferences with persisted stores in Svelte #sveltejs #javascript #typescript
We're excited to announce general availability of Pace #launch #preview #project-management
Batching operations in Go #Golang #Patterns