🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup

Filtering a reference dropdown based on another selected value in Sanity.io

14 replies
Last updated: May 21, 2021
Hi! I have a
release
document that references 2 documents:
game
and
gameMode
. I am trying to show only the
gameMode
documents referenced by the currently selected
game
in the Game Mode dropdown.I am using the filter option of the reference type (
https://www.sanity.io/docs/reference-type ) as an async function to get the _ref of the currently selected
game
in the
release
document.I then pass it to the params so that the filter can query for the referenced
gameMode
of that currently selected
game
. These
gameMode
are part of an array of type reference called
gameModes
in
game
.The GROQ query in the filter returns that
gameModes
array with the correct
gameMode
references but the dropdown shows the little loader and then nothing. What is expected from this filter? Is there a problem with my GROQ query?

export default {
  title: 'Release',
  name: 'release',
  type: 'document',
  fields: [
    {
      title: 'Game',
      name: 'game',
      type: 'reference',
      to: [{ type: 'game' }],
    },
    {
      title: 'Game Mode',
      name: 'gameMode',
      type: 'reference',
      to: [{ type: 'gameMode' }],
      options: {
        filter: async ({ document }) => {
          const query = '*[_id == $id]{"gameRef": $gameRef}';
          const params = { id: document._id, gameRef: document.game._ref };
          const gameRef = await client.fetch(query, params);
          return {
            filter: `* [_type == "game" && _id == $gameRef]{gameModes[]->}`,
            params: {
              gameRef,
            },
          };
        },
      },
    },
  ],
};
May 19, 2021, 9:43 PM
Does it work using the studio search? I'm not sure if this is applicable beyond that
Additional GROQ-filter to use when searching for target documents
May 19, 2021, 9:50 PM
Hey
user L
the query work when I use the Vision plugin in the studio
May 19, 2021, 10:00 PM
Right, but where are expecting to see the filtered result, in the studio document list or in the search?
May 19, 2021, 10:01 PM
oh sorry I see yes in the studio document
May 19, 2021, 10:02 PM
The filter in your return is just the filter (the stuff in
[
and
]
in your GROQ queries), so you won’t include
*[
,
]
, or projections.
May 19, 2021, 11:14 PM
Hey thanks
user A
! I'm not sure how to go about getting the
gameMode
documents that are referenced in the
gameModes
array then. Also in the return filter query I guess it does not make sense that I am fetching `_type == "game"`because the type is inferred from
gameMode
right?
May 20, 2021, 9:18 PM
If I understand your schema correctly, I think something like this should work:

options: {
  filter: async ({ document }) => {
    const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)

    return {
      filter: '@._id in $list',
      params: {list: includedRefs},
    };
  },
},
You’ll want to add error handling for when the user tries to select
gameMode
before selecting
game
.
May 20, 2021, 10:37 PM
If I understand your schema correctly, I think something like this should work:

options: {
  filter: async ({ document }) => {
    const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)

    return {
      filter: '@._id in $list',
      params: {list: includedRefs},
    };
  },
},
You’ll want to add error handling for when the user tries to select
gameMode
before selecting
game
.
May 20, 2021, 10:37 PM
If you want to disable the filter if there’s no
game
selected (i.e., show everything), you could put this at the start of your filter function:

if (!document.game) return true
Also, it’s worth noting that this will not handle a case where a user selects a
game
, chooses a
gameMode
, and then changes
game
to something else (or to null). A custom input component might be the best option here, which could disable
game
if
gameMode
has a selection.
May 20, 2021, 10:41 PM
I should explain what this is doing for anyone else reading this:

{
  title: 'Game Mode',
  name: 'gameMode',
  type: 'reference',
  to: [{ type: 'gameMode' }],
Every filter (in
this sense of the word) must start with a reference. That reference will pull in all documents from one or more document types. In the case above, it’s all documents of type
gameMode
.

filter: async ({ document }) => {
We’re using client.fetch, so we need to make this async. It’s important to import
sanityClient
then create a
client
variable that’s versioned .

const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)
This is fetching all documents that are of type
game
that have an
_id
equal to the one selected in the
game
filter earlier on this page. From those documents, it’s traversing the
gameMode
array (which is an array of references) and returning an array of `_ref`s (in this case, storing the array in the variable
includedRefs
). This is useful because the
_ref
value of a reference is equal to the
_id
value of the source document.

return {
  filter: '@._id in $list',
  params: {list: includedRefs},
};
This is taking that array of `_ref`s we got using client.fetch and then filtering the
_id
of each item in the reference dropdown against that list. Only the ones that match are shown. You can also exclude items from a list (I use a variation of this to remove any references from a dropdown that have already been selected, because why show them again?) by changing filter to
'!(@._id in $list)'
.
May 20, 2021, 10:57 PM
Thank you very much
user A
! This is working🚀 The only thing I had to change is
gameMode
in
const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)
is actually
gameModes
, it is an array of references to
gameMode
in a
game
type (to clarify if anybody is following)Out of curiosity is it possible to constrain a list of documents in an array of type reference based on another selected value. I did not see a filter option for the array type in the doc?
May 20, 2021, 11:22 PM
As long as they’re all still references, I think you could. The filter would need to be applied on the reference (as opposed to the array) like it was in the example above, but I expect it would work with some slight changes. Are you saying that rather than being a single-item reference,
gameMode
would be an array of references?
May 21, 2021, 4:42 AM
As long as they’re all still references, I think you could. The filter would need to be applied on the reference (as opposed to the array) like it was in the example above, but I expect it would work with some slight changes. Are you saying that rather than being a single-item reference,
gameMode
would be an array of references?
May 21, 2021, 4:42 AM
Yes exactly let's say I wanted to select multiple item
May 21, 2021, 3:33 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?