Is it possible to generate a field value of a document every time it is edited in Sanity.io?
13 replies
Last updated: Feb 12, 2021
J
I'm wondering if it's possible to generate a field value of a document every time the document is edited? Kind of like an 'onChange' hook? Context: I generate URL paths based on a document's nested categories, but these are complex to query with GROQ from our client. For this reason I'd like to add a
pathfield / value to these documents, which I'd need to update any time the document/the nested categories are modified.
Feb 11, 2021, 11:12 PM
J
Hi User. Just to clarify, when you say edited, do you mean edited and published?
Feb 11, 2021, 11:20 PM
J
user E
, yes, I sent you a DM, I have code to give to given to me by User, the great 🙂Feb 12, 2021, 1:21 AM
J
user A
Either would be fine, as long as I can make changes that go live once the document is published, which could be either directly upon publishing or if published later.Feb 12, 2021, 8:10 AM
J
user L
cool, I've replied in DM, but perhaps you can share the code publicly here too for anyone searching in the future.Feb 12, 2021, 8:21 AM
J
Ok, so you should create a file and include that at the top of he schema file:
Next, addÂ
import UpdateRelatedFields from '../../../plugins/updateRelatedFields/UpdateRelatedFields'
inputComponent to the schema field:
{ name: 'related', title: 'Related', type: 'string', inputComponent: DisplayRelatedFields },
Feb 12, 2021, 7:14 PM
J
Then, here is the code for the updater:
You need this:
Here, the onBlur makes it happen:
[and
And this is what does the update:
import PropTypes from 'prop-types' import React from 'react' // eslint-disable-next-line import/no-unresolved import { FormBuilderInput, withDocument, withValuePath } // eslint-disable-next-line import/no-unresolved from 'part:@sanity/form-builder' // eslint-disable-next-line import/no-unresolved import client from 'part:@sanity/base/client' const handleUpdateRelatedFields = (value, props) => { if (props.document._id === undefined) return const {document} = props const {title} = document client .patch(props.document._id) .setIfMissing({ metaTitle: title, careers: { title: `Rollins ${title} Careers` }, metaDescription: title, metaKeywords: [title] }) .commit() .catch((error) => console.log(error)) } class UpdateRelatedFields extends React.Component { // Declare shape of React properties // eslint-disable-next-line import/no-unresolved static propTypes = { type: PropTypes.shape({ title: PropTypes.string }).isRequired, level: PropTypes.number, focusPath: PropTypes.array } render () { const {value, level, focusPath, onFocus, onChange} = this.props const {inputComponent, ...type} = this.props.type // Render component return ( <div style={{marginBottom: 20}}> <FormBuilderInput level={level} type={type} value={value} onChange={onChange} path={[]} focusPath={focusPath} onFocus={onFocus} onBlur={(event) => handleUpdateRelatedFields(event, this.props)} /> </div> ) } } export default withValuePath(withDocument(UpdateRelatedFields))
import PropTypes from 'prop-types' import React from 'react' // eslint-disable-next-line import/no-unresolved import { FormBuilderInput, withDocument, withValuePath }
// Render component return ( <div style={{marginBottom: 20}}> <FormBuilderInput level={level} type={type} value={value} onChange={onChange} path={[]} focusPath={focusPath} onFocus={onFocus} onBlur={(event) => handleUpdateRelatedFields(event, this.props)} /> </div> ) }
handleUpdateRelatedFieldspasses the event and props....
And this is what does the update:
const {title} = document client .patch(props.document._id) .setIfMissing({ metaTitle: title, careers: { title: `Rollins ${title} Careers` }, metaDescription: title, metaKeywords: [title] }) .commit() .catch((error) => console.log(error))
Feb 12, 2021, 7:15 PM
J
user T
there is a new/better way to do this with Sanity UI components? That's a lot of code to update one field based on another.Feb 12, 2021, 7:16 PM
J
That's the intended outcome of the UI project, but it's not something someone should wait on when working on something.
The current level UI should be used for is replacing any custom markup and/or styling. If you go looking into the the various pieces of form-builder are working now, you can see some of UI in action (we're in the process of putting all the default form pieces into UI).
As an example, here's the component return for a Slug schema type:
The current level UI should be used for is replacing any custom markup and/or styling. If you go looking into the the various pieces of form-builder are working now, you can see some of UI in action (we're in the process of putting all the default form pieces into UI).
As an example, here's the component return for a Slug schema type:
<FormField title={type.title} description={type.description} level={level} __unstable_markers={markers} __unstable_presence={presence} inputId={inputId} > <Stack space={3}> <Flex> <Box flex={1}> <TextInput id={inputId} ref={forwardedRef} customValidity={errors.length > 0 ? errors[0].item.message : ''} disabled={isUpdating} onChange={handleChange} onFocus={handleFocus} value={value?.current || ''} readOnly={readOnly} /> {generateState?.status === 'error' && ( <Card padding={2} tone="critical"> {generateState.error.message} </Card> )} </Box> <Box marginLeft={1}> <Button mode="ghost" type="button" disabled={readOnly || isUpdating} onClick={handleGenerateSlug} onFocus={handleFocus} text={generateState?.status === 'pending' ? 'Generating…' : 'Generate'} /> </Box> </Flex> </Stack> </FormField>
Feb 12, 2021, 7:33 PM
J
Thanks, User!
Feb 12, 2021, 7:34 PM
J
Similarly, if we have users and groups, we can add a relation to groups in the user schema, or vice versa, or we can create a "person-in-the-middle", userGroup and then have a third screen where we can add a user to a group, but that's inconvenient. I have original code from User on how to display "related" or "used in" fields to, for example, display all users in a group from the user side, but I was wondering if there's a new/better way to do that...
Feb 12, 2021, 8:02 PM
J
I know that new media manager is doing that, so maybe that's a hint. Ideally, we would bidirectional access to both, namely: A) being able to add/delete/edit a user from the group side, and b) add/delete/edit a group from the user side, so any thoughts on how to do this better than what we have from now more than a year ago would be great.
Feb 12, 2021, 8:03 PM
J
user T
I'm using User's suggestion, of which I might change parts to use the new Sanity UI. I'm using withDocument in an attempt to use the document's values to patch the value of a form field, but I need to follow references to get the values I need. Is there any way to follow references in withDocument? Or to query references from within a custom input component?Feb 12, 2021, 8:24 PM
J
I found a
useRefValuehook somewhere deep in the Sanity packages codebase, which let's me do what I'm looking for 🙂 https://github.com/sanity-io/sanity/blob/next/packages/@sanity/field/src/diff/hooks/hooks.ts / https://github.com/sanity-io/sanity/blob/next/packages/@sanity/field/src/types/file/diff/FileFieldDiff.tsx
Feb 12, 2021, 9:04 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.