Grouper component for Svelte.

David Hernandez · Mat Ryer · 1 Feb 2020

Grouper component for Svelte.

Mat Ryer · David Hernandez · 1 Feb 2020

The problem

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.

Grouper component

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}

Grouping data

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.

Rendering the view

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.

Using Grouper

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 group
  • groupForItem - (required function) a Group function that returns a group for the given item
  • let:group={group} - Gives the slots inside the Grouper access to the group
  • let: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)

Group function

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
}
  • This function should return a unique string if it is to work within keyed {each} blocks.

The item slot <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>

The group slot <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.


Learn more about what we're doing at Pace.

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


First published on 1 Feb 2020 by Mat Ryer David Hernandez
#Tech #Svelte

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.

https://pace.dev/blog/2020/02/01/grouper-component-for-svelte-by-mat-ryer.html


You might also like:

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

Subscribe:
Atom RSS JSON