Unlock seamless workflows and faster delivery with our latest releases ā€“ get the details

Filtering a reference list with dynamic filter in Sanity.io

13 replies
Last updated: Jan 28, 2021
Hey all! Real quick question for you. I'm trying to filter a reference list with a dynamic filter. This is where I'm currently at but it doesn't seem to do anything right now:
filter: ({ document }) => {
  const ref = document.content?.product?._ref
  if (!ref) return
  return {
    filter: 'product._ref == $product',
    params: {
      product: ref,
    },
  }
},
The document that this is being run within has a field
content.product
which references a
product
, and the document type I am trying to filter here should also reference the same
product
. You can't use
console.log
within filter functions, so I have no way to be able to inspect what
document
contains. I've tried a number of different ways of going about this, including checking for the presence of
document.content
and
document.content.product
first, but nothing seems to make a difference here.
Any ideas, anyone?
Jan 28, 2021, 1:49 PM
The filter setup seems correct. Could you share your schema definition for the relevant document as well as the
product
document type?
Jan 28, 2021, 2:19 PM
The filter setup seems correct. Could you share your schema definition for the relevant document as well as the
product
document type?
Jan 28, 2021, 2:19 PM
Schema for document:
Jan 28, 2021, 2:27 PM
import React from 'react'
import Tabs from 'sanity-plugin-tabs'

const IntroStyle = (props) => (
  <p style={{ textAlign: 'center', fontSize: '1.25rem', fontWeight: '600' }}>
    {props.children}
  </p>
)

export const ProductPage = {
  title: 'Product Page',
  name: 'productPage',
  type: 'document',
  fields: [
    {
      name: 'content',
      type: 'object',
      inputComponent: Tabs,

      fieldsets: [
        { name: 'main', title: 'Main', options: { sortOrder: 10 } },
        { name: 'meta', title: 'Meta', options: { sortOrder: 30 } },
        { name: 'config', title: 'Options', options: { sortOrder: 50 } },
      ],

      fields: [
        {
          title: 'Product',
          name: 'product',
          type: 'reference',
          to: [{ type: 'product' }],
          fieldset: 'main',
          description: 'Select the product that this product page is about',
          options: {
            filter: `count(*[_type == 'productPage' && references(^._id)]) == 0`,
          },
        },
        {
          title: 'Page title',
          name: 'title',
          type: 'string',
          fieldset: 'main',
          description:
            '[OPTIONAL] Customise the title of this product page, if you do not want to use the product name',
        },
        {
          title: 'Parent page',
          name: 'parentPage',
          type: 'reference',
          to: [{ type: 'page' }, { type: 'productPage' }],
          description:
            '[optional] set a parent page for this page to appear below in url structure',
          fieldset: 'main',
        },
        {
          title: 'Page content',
          name: 'pageContent',
          type: 'array',
          of: [
            {
              type: 'block',
              styles: [
                { title: 'Normal', value: 'normal' },
                {
                  title: 'Intro',
                  value: 'intro',
                  blockEditor: {
                    render: IntroStyle,
                  },
                },
                { title: 'H2', value: 'h2' },
                { title: 'H3', value: 'h3' },
                { title: 'H4', value: 'h4' },
                { title: 'Quote', value: 'quote' },
              ],
            },
            { type: 'productPage.hero' },
            { type: 'productPage.intro' },
            { type: 'columns' },
            {
              type: 'image',
              fields: [
                {
                  title: 'Alt text',
                  name: 'alt',
                  type: 'text',
                  options: {
                    isHighlighted: true,
                  },
                },
              ],
              options: {
                metadata: ['palette'],
                hotspot: true,
              },
            },
            { type: 'video' },
            { type: 'gallery' },
            { type: 'page.imageSlider' },
            { type: 'page.richTextWithMedia' },
          ],
          fieldset: 'main',
        },
        {
          title: 'SEO / Meta Settings',
          name: 'meta',
          type: 'page.meta',
          fieldset: 'meta',
        },
        {
          title: 'Enable sticky "Get a Quote" CTA?',
          name: 'hasStickyQuoteButton',
          type: 'boolean',
          description:
            '[optional] Enable the sticky header (desktop) / footer (mobile) Get a Quote CTA on this page',
          fieldset: 'config',
        },
        {
          title: 'Technical files',
          name: 'technicalFiles',
          description:
            '[optional] Select which technical files associated with this product to show on the product page',
          type: 'array',
          of: [
            {
              title: 'File',
              type: 'reference',
              to: [
                {
                  type: 'technicalFile',
                  options: {
                    filter: ({ document }) => {
                      const ref = document.content?.product?._ref
                      if (!ref) return {}
                      return {
                        filter: 'product._ref == $product',
                        params: {
                          product: ref,
                        },
                      }
                    },
                  },
                },
              ],
            },
          ],
          fieldset: 'config',
        },
      ],
    },
  ],
  preview: {
    select: {
      pageTitle: 'content.title',
      productTitle: 'content.product.title',
    },
    prepare(selection) {
      const { pageTitle, productTitle } = selection
      return {
        title: pageTitle || productTitle,
      }
    },
  },
}
Jan 28, 2021, 2:27 PM
Technical file schema:
export const TechnicalFile = {
  title: 'File',
  name: 'technicalFile',
  type: 'document',
  fields: [
    {
      title: 'Document Title',
      name: 'title',
      type: 'string',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'File',
      name: 'file',
      type: 'file',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Document Code',
      name: 'code',
      type: 'string',
    },
    {
      title: 'Product',
      name: 'product',
      type: 'reference',
      to: [{ type: 'product' }],
    },
    {
      title: 'Document Type',
      name: 'doctype',
      type: 'string',
      options: {
        list: [
          'Installation instructions',
          'Technical drawings',
          'CAD file',
          'Product data sheet',
        ],
      },
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Description',
      name: 'description',
      type: 'text',
    },
    {
      title: 'Image',
      name: 'image',
      description:
        '[optional] Add an image to override the default product image to be shown in the file listing',
      type: 'image',
      fields: [
        {
          title: 'Alt text',
          name: 'alt',
          type: 'text',
          options: {
            isHighlighted: true,
          },
        },
      ],
      options: {
        metadata: ['palette'],
        hotspot: true,
      },
    },
  ],
}
Jan 28, 2021, 2:28 PM
Product is pretty much placeholder right now:
export const Product = {
  title: 'Product',
  name: 'product',
  type: 'document',
  fields: [
    {
      title: 'Product name',
      name: 'title',
      type: 'string',
      validation: (Rule) => Rule.required(),
    },
    {
      title: 'Featured image',
      name: 'image',
      type: 'image',
      fields: [
        {
          title: 'Alt text',
          name: 'alt',
          type: 'text',
          options: {
            isHighlighted: true,
          },
        },
      ],
      options: {
        metadata: ['palette'],
        hotspot: true,
      },
      validation: (Rule) => Rule.required(),
    },
  ],
}
Jan 28, 2021, 2:29 PM
To be clear, the document selector currently shows all documents of type
technicalFile
, rather than filtering them.
Jan 28, 2021, 2:35 PM
Could you try moving your
options
out of the
to[]
array? So one level higher?
Jan 28, 2021, 2:36 PM
As follows:
{
  title: 'File',
  type: 'reference',
  to: { type: 'technicalFile' },
  options: {
    filter: ({ document }) => {
      const ref = document.content?.product?._ref
      if (!ref) return {}
      return {
        filter: 'product._ref == $product',
        params: {
          product: ref,
        },
      }
    },
  },
},
Jan 28, 2021, 2:39 PM
Yes, that's it. I'm an idiot and didn't see that I had it in the wrong place entirely! Thanks so much
user M
!
Jan 28, 2021, 2:41 PM
An error would have helped I'm sure - not sure why it worked before šŸ˜„
Jan 28, 2021, 2:43 PM
It didn't. That was poor wording on my part. Seeing as Sanity is fully TypeScripted up now, this feels like something that could potentially be fairly easy to catch, as it is a property in a place that it shouldn't be
Jan 28, 2021, 2:50 PM
Yep, agreed. We could definitely improve error messages here and save some hair-pulling šŸ˜‰
Jan 28, 2021, 3:01 PM

Sanityā€“ build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?