7.5 KiB
description, tags, skill_level, directus_version, author_override, author
| description | tags | skill_level | directus_version | author_override | author |
|---|---|---|---|---|---|
| This recipe explains how to construct a documentation content hierarchy by using Tree View, the M2O alias for creating recursive relationships. | 9.18.1 | Eron Powell |
Build a Content Hierarchy
{{ $frontmatter.description }}
:::tip Author: {{$frontmatter.author}}
Directus Version: {{$frontmatter.directus_version}}
:::
Explanation
In documentation, it is incredibly common (if not the standard) to have a hierarchy of content or data: a parent page, with child pages.
If you're working with markdown files or an SSG, content hierarchies are created easily with files and folders. If your content is in a database, you can create a hierarchy of nested items with a recursive O2M relationship, which is the focus of this recipe.
In Directus, there is a special O2M alias field called Tree View, which makes it easier to create and manage this type of recursive relationship.
For this recipe, we'll use Tree View to build a simple content hierarchy for documentation:
App OverviewConfigurationFlowsData ModelCollectionsFields
Here's the general outline of what's to come:
- Use Tree View to create a recursive relationship in a collection called
docs. - Add
parent<->childitems to the collection, using that recursive relationship. - Give the public role read permissions on
docs, so users can access them.
At the end, in Final Tips, we'll point out some additional steps you can take to make your content hierarchy easier to work with in a frontend.
The Recipe
:::tip Requirements
You'll need to be familiar with permissions.
:::
- Create a collection and add fields as follows:
docs
- id
- title (a STRING input field)
- parent_id (an integer input field)
- children (a Tree View O2M alias field, configured with docs.parent_id as the foreign key and the O2M Interface)
- body (optional: a text input field with a Markdown interface)
-
Configure the public role with read permissions for the
docscollection. -
Create nested items on the
docscollection as desired:- To create top-level parent items, create an item as usual.
- To add a nested child, edit the parent item and click Create New or Add Existing.
-
Optional: manually sort items into desired order.
Final Tips
Now that your nested content is created, you'll need to access and use it in the frontend. Here are two key points to consider.
Fetch Nested Data
A basic api call to get items, such as /items/docs/, returns flat data:
:::details Toggle open to view flat data.
{
"data": [
{
"id": 1,
"title": "App Overview",
"parent_id": null,
"body": "# App Overview\n\nGeneral info about the app.",
"sort": 1,
"children": []
},
{
"id": 2,
"title": "Config Overview",
"parent_id": null,
"body": "# Config Overview\n\nThere are many config options available",
"sort": 2,
"children": [3]
},
{
"id": 3,
"title": "Data Model",
"parent_id": 2,
"body": "# How the Data Model Works\n\n",
"sort": 3,
"children": [4, 5]
},
{
"id": 4,
"title": "Fields",
"parent_id": 3,
"body": "# How Fields Work",
"sort": 4,
"children": []
},
{
"id": 5,
"title": "Collections",
"parent_id": 3,
"body": "# Collections\n\nThey're data tables.",
"sort": 5,
"children": []
}
]
}
:::
To fix this, you could write an algorithm that iterates through each item in the data array and re-nests it on the frontend. But that may be a bit resource intensive, depending on your collection's data. Alternatively, we can add query parameters on the Directus API call to nest it for us. In this case, we could make an api call like this:
/items/docs?
filter[parent_id][_null]=true
&fields=id,title,body,sort,
children.id,children.body,children.title,children.sort,
children.children.id,children.children.title,children.children.body,children.children.sort, children.children.children
Which will return data properly nested as seen here:
:::details Toggle open to view nested data
{
"data": [
{
"id": 1,
"name": "App Overview",
"body": "# App Overview\n\nGeneral info about the app.",
"sort": 1,
"nest": []
},
{
"id": 2,
"name": "Config Overview",
"body": "# Config Overview\n\nMany config options available",
"sort": 2,
"nest": [
{
"id": 3,
"body": "# How the Data Model Works",
"name": "Data Model",
"sort": 3,
"nest": [
{
"id": 4,
"name": "Fields",
"body": "# How Fields Work",
"sort": 4,
"nest": []
},
{
"id": 5,
"name": "Collections",
"body": "# Collections\n\nThey're data tables.",
"sort": 5,
"nest": []
}
]
}
]
}
]
}
:::
In the call above, the filter[parent_id][_null]=true query parameter serves to filter
non-parents at the first level. The fields query parameters let us extract specific
fields, including nested relational fields. There are two key things to note with this type of query:
- We knew our content hierarchy was three levels deep. You'll also need to be aware of (and account for) the depth of your content hierarchy and make the api call accordingly.
- The
parent_idforeign key field was not added, as it would re-nest parent item data under its own child items, which is duplicate and useless.
Create Relative Paths
Nested content by itself is nice, but it would be helpful if we could create a path for each item, for routing in frontend navigation. To do this, you could use flows to create a path dynamically. Here's one general approach:
- Add a
pathfield to thedocscollection and configure it so edits are turned off on the item detail page. - Configure a flow with an
Event Hooktrigger, which runs onitems.createanditems.updatefor thedocscollection. - Add operations to the flow to read the parent's path of the item being created or updated, append the item's title,
write the new path into the present item's
pathfield value, then update the paths of its children (if it has any children).
:::tip
In this recipe, for the sake of a clean visual example, we created a body field directly on the docs collection.
However, you may find it more user-friendly to keep the markdown content in another relationally-linked collection, then
just use Tree View to store the path and/or keep track of the content hierarchy.
:::