Filtering a reference dropdown based on another selected value in Sanity.io
14 replies
Last updated: May 21, 2021
F
Hi! I have a
https://www.sanity.io/docs/reference-type ) as an async function to get the _ref of the currently selected
releasedocument that references 2 documents:
gameand
gameMode. I am trying to show only the
gameModedocuments referenced by the currently selected
gamein 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
gamein the
releasedocument.I then pass it to the params so that the filter can query for the referenced
gameModeof that currently selected
game. These
gameModeare part of an array of type reference called
gameModesin
game.The GROQ query in the filter returns that
gameModesarray with the correct
gameModereferences 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
C
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
F
Hey
user L
the query work when I use the Vision plugin in the studioMay 19, 2021, 10:00 PM
C
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
F
oh sorry I see yes in the studio document
May 19, 2021, 10:02 PM
G
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
F
Hey thanks
user A
! I'm not sure how to go about getting the gameModedocuments that are referenced in the
gameModesarray 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
gameModeright?
May 20, 2021, 9:18 PM
G
If I understand your schema correctly, I think something like this should work:
You’ll want to add error handling for when the user tries to select
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}, }; }, },
gameModebefore selecting
game.
May 20, 2021, 10:37 PM
G
If I understand your schema correctly, I think something like this should work:
You’ll want to add error handling for when the user tries to select
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}, }; }, },
gameModebefore selecting
game.
May 20, 2021, 10:37 PM
G
If you want to disable the filter if there’s no
Also, it’s worth noting that this will not handle a case where a user selects a
gameselected (i.e., show everything), you could put this at the start of your filter function:
if (!document.game) return true
game, chooses a
gameMode, and then changes
gameto something else (or to null). A custom input component might be the best option here, which could disable
gameif
gameModehas a selection.
May 20, 2021, 10:41 PM
G
I should explain what this is doing for anyone else reading this:
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
We’re using client.fetch, so we need to make this async. It’s important to import
This is fetching all documents that are of type
This is taking that array of `_ref`s we got using client.fetch and then filtering the
{ title: 'Game Mode', name: 'gameMode', type: 'reference', to: [{ type: 'gameMode' }],
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 }) => {
sanityClientthen create a
clientvariable that’s versioned .
const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)
gamethat have an
_idequal to the one selected in the
gamefilter earlier on this page. From those documents, it’s traversing the
gameModearray (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
_refvalue of a reference is equal to the
_idvalue of the source document.
return { filter: '@._id in $list', params: {list: includedRefs}, };
_idof 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
F
Thank you very much
is actually
user A
! This is working🚀 The only thing I had to change is gameModein
const includedRefs = await client.fetch(`*[_type == 'game' && _id == "${document.game._ref}"].gameMode[]._ref`)
gameModes, it is an array of references to
gameModein a
gametype (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
G
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,
gameModewould be an array of references?
May 21, 2021, 4:42 AM
G
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,
gameModewould be an array of references?
May 21, 2021, 4:42 AM
F
Yes exactly let's say I wanted to select multiple item
May 21, 2021, 3:33 PM
Sanity– build remarkable experiences at scale
The Sanity Composable Content Cloud is the headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.