4 min read

Drawer Collection Component

Select an item from a collection in your extension with this native Directus component.

The v-drawer-collection component is a specialized dialog in Directus that enables users to select items from a database collection. It provides a rich interface for browsing, searching, and filtering collection data before selecting one or more items.

Key Use Cases:

  • Relationship field pickers (one-to-many, many-to-many)
  • Item selection in modal dialogs
  • Filtered collection browsing
  • Single or multiple item selection modes

Component API

Props

PropTypeDefaultDescription
activebooleanfalseControls whether the drawer is open. When undefined, internal state is used.
selection(number | string)[][]Array of currently selected item IDs.
collectionstringrequiredThe database collection name to display.
multiplebooleanfalseWhen true, allows selecting multiple items. When false, only one item can be selected.
filterFilternullAdditional system filters to apply to the collection query.
drawerPropsVDrawerProps{}Additional props to pass to the underlying VDrawer component.

Emits

EventPayloadDescription
update:activebooleanFired when the drawer's open/closed state changes.
input(number | string)[] | nullFired when the user confirms selection. Contains the selected item IDs.

How It Works

Selection Management

The component maintains internal state for selections and tracks whether the user has made changes since opening. Key behaviors:

  • Initial Load: When the drawer opens, the current selection is stored as the "initial selection"
  • Change Tracking: A hasSelectionChanged computed property tracks if the selection differs from when the drawer opened
  • Save: The @apply event (from v-drawer) triggers a save, which emits the input event only if changes were made
  • Cancel: The @cancel event closes the drawer without emitting input

Layout System

The component uses Directus's layout system to display collection items from the global or user's preset:

  • Supports multiple layout types (tabular, card, etc.) via layout-${type} components
  • Users can switch layouts using layout action buttons on the collection which will be adopted by the drawer as well.
  • Layout-specific options and queries are managed locally and are not remembered outside the drawer.
  • Filters are combined from both user preferences and system permissions

Search & Filtering

  • Search: A search input allows real-time text filtering
  • Filters: System filters and user-preset filters are merged
  • Dynamic Updates: Layout options and queries update reactively as users interact

Usage Example

Basic Single-Item Selection

The Drawer Collection is available to extensions without needing to import in your script. Here is an example of a basic single item selection drawer:

<script setup lang="ts">
import { ref } from 'vue';

const isDrawerOpen = ref(false);
const selectedAuthorId = ref<string | number | null>(null);

const handleItemSelected = (selectedIds: (string | number)[] | null) => {
  if (selectedIds && selectedIds.length > 0) {
    selectedAuthorId.value = selectedIds[0];
    console.log(`Selected author ID: ${selectedAuthorId.value}`);
  }
};
</script>

<template>
  <div>
    <button @click="isDrawerOpen = true">
      Select an Author
    </button>

    <v-drawer-collection
      v-model:active="isDrawerOpen"
      :selection="selectedAuthorId ? [selectedAuthorId] : []"
      collection="authors"
      @input="handleItemSelected"
    />

    <div v-if="selectedAuthorId">
      Selected Author ID: {{ selectedAuthorId }}
    </div>
  </div>
</template>

Multiple Selection with Filtering

If you want to accept multiple selection from the drawer collection, use the following example:

<script setup lang="ts">
import { ref } from 'vue';
import type { Filter } from '@directus/types';

const isDrawerOpen = ref(false);
const selectedUserIds = ref<(string | number)[]>([]);

// Only show active users (status = 'active')
const activeUsersFilter: Filter = {
  status: { _eq: 'active' }
};

const handleUsersSelected = (selectedIds: (string | number)[] | null) => {
  if (selectedIds) {
    selectedUserIds.value = selectedIds;
    console.log(`Selected ${selectedIds.length} users`);
  }
};
</script>

<template>
  <div>
    <button @click="isDrawerOpen = true">
      Select Users
    </button>

    <v-drawer-collection
      v-model:active="isDrawerOpen"
      :selection="selectedUserIds"
      :multiple="true"
      collection="directus_users"
      :filter="activeUsersFilter"
      @input="handleUsersSelected"
    />

    <div v-if="selectedUserIds.length > 0">
      <p>Selected {{ selectedUserIds.length }} users:</p>
      <ul>
        <li v-for="id in selectedUserIds" :key="id">
          User ID: {{ id }}
        </li>
      </ul>
    </div>
  </div>
</template>

Advanced: Custom Drawer Styling

If you want to make some changes to the icon or title of the Drawer, you can use the drawer-props attribute. Here is an example:

<script setup lang="ts">
import { ref } from 'vue';
import DrawerCollection from '@/views/private/components/drawer-collection.vue';

const isDrawerOpen = ref(false);
const selectedItemId = ref<string | number | null>(null);

const customDrawerProps = {
  icon: 'link',
  title: 'Link Related Item',
};

const handleSelect = (selectedIds: (string | number)[] | null) => {
  if (selectedIds && selectedIds[0]) {
    selectedItemId.value = selectedIds[0];
  }
};
</script>

<template>
  <DrawerCollection
    v-model:active="isDrawerOpen"
    :selection="selectedItemId ? [selectedItemId] : []"
    collection="projects"
    :drawer-props="customDrawerProps"
    @input="handleSelect"
  />
</template>

Internal Architecture

Composables Used

  • useCollection() - Retrieves collection metadata (name, icon, etc.)
  • useLayout() - Manages layout rendering and state
  • usePreset() - Handles user layout preferences, search, and filters

Key Slots

The component forwards all slots from its parent, allowing custom content injection:

<v-drawer-collection collection="authors">
  <template #title>Custom title content</template>
  <template #subtitle>Custom subtitle content</template>
  <template #title-outer:prepend>Custom content before the title container</template>
  <template #actions:prepend>Custom content before the actions</template>
  <template #actions>Custom content in the actions container</template>
  <template #actions:append>Custom content after the actions</template>
  <template #header:append>Custom content after the header</template>
  <template #sidebar>Custom content for the sidebar</template>
</v-drawer-collection>

Important Behaviors

  1. Drawer State Sync: The v-model:active binding keeps the drawer state in sync with parent components
  2. Selection Stability: Selection is only committed when the user clicks the tick button (apply action)
  3. Filter Merging: User filters are merged with their permissions, allowing flexible and secure query building
  4. No Results Handling: A helpful info message displays when no items match the filter criteria
  5. Dynamic Layouts: Users can switch between different layout types within the drawer

Performance Considerations

  • Selections are stored locally and only emitted on confirmation
  • Layout options are cached and updated only when necessary
  • The component respects the collection's configured display settings
Send me a coffee