What I would like to achieve is – obviously – against the design of the Payloads API but i have found no other way to make the same UX.
I have a collection called
jobs
this collection has a field
listings
this field is a custom field which used an external api to populate data into a select field. This was the easy part.
Now, what I would like to do is listening for the changes of this
listings
field, and then give value from an API to other fields when it is changed.
The use case is: i have job listings collection which uses Workable API to get the current openings, so when you click on New button you should first select the listing, then the field would trigger a fetch to get the selected listing
Example:
export const Jobs: CollectionConfig = {
slug: 'jobs',
fields: [
// What this does is, upon creation calls an API to fill itself with data.
// Once the value changes, i would call an API again to give value for the `title` for example
workableListings({
name: 'listings',
label: 'Select a workable listing',
}),
{
type: 'text',
label: 'Title',
name: 'title',
defaultValue: ''
},
],
}
Unfortunately all the hooks run upon "save" button, so there is no way to use hooks for this. Nor i found any way to "call back" to fields when the field change
Okay I figured it out.
I don't have to go back to fields to manipulate the other fields. Instead within my custom field i can use the
useAllFormFields
hook to update other fields from my third-party source.
Note for future solution lurkers: this solution is pretty sketchy because once the external API changes the whole thing would just collapse.
do you have some example code for this? looking to implement a solution for this, having a collection in payload populated with data/items from an external api
the defaultValue on fields can take a function that would allow you to async pull data
You can use async defaultValue functions to fill fields with data from API requests.
Also, similar thread here
https://discord.com/channels/967097582721572934/1031993785971900556/threads/1237402299945582746i'm looking to have a
positions
collection in payloadCMS which fetches data/positions from another system that our HR department uses to create and manage positions.
We are just getting started using the new HR system, but I know they've got both a robust API and webhooks support. would just like to look at some example code, sounded like OP here had a similar case going on
For sure, I'm happy to make an example if you describe how the field should work
I'm not sure if there is an official example, but I can also look
hey, happy to show ours. lemme put it together
i'm not 100 % on all fields, but i guess that from the external system a position would have basic info like a title, an office, an expiration date and maybe a department and a contact person etc. I imagine I'll have to create fields in Payload that copy their structure closely so they are easier to populate.
then we probably want to extend the positions further with some additional data (tags, images etc), and this I imagine will be done in Payload - extra data for use on our website, a more presentational context
Yeah I've done something similar to that, totally doable
lovely! i've always figured it's doable, just a bit blue on where to start/how to code it in
You could try with a hook to start, beforeRead
So that you can fire an event before the document loads
Or you can just do it on a field-by-field basis
Youll need to know what data you need to pull
So that will likely be a field, maybe an ID
then based on the value of the ID field, you can populate the other fields in the collection in one-go
// Created a custom field which looks like this:
const workableListings = ({ name, label }: { name: string; label?: string }): TField => ({
name,
label: label || name,
type: 'text',
defaultValue: 'empty',
required: true,
validate: (value: string) => (value !== 'empty' ? true : 'Select a listing to continue'),
admin: {
components: {
Field, // Field is where the magi happens
Cell, // the cell is basically empty
},
},
})
// Field.tsx
const Field: React.FC<Props> = ({ path, label, required, validate }) => {
const {
value = '',
setValue,
errorMessage,
showError,
} = useFieldType({
path,
validate,
})
const { submit } = useForm()
async function saveAsDraft() {
await submit({ overrides: { draft: true } })
}
useEffect(function initialize() {
const fetchData = async () => {
const response = await fetch('/api/careers')
const data = await response.json()
setListings(data.jobs)
}
fetchData()
}, [])
useEffect(
function updateLatestFields() {
latestFields.current = fields
},
[fields],
)
const fetchListingAndUpdateFields = async (id: string) => {
const res = await fetch(`/api/careers/${id}`)
const career = await res.json()
Object.entries(PATH_TO_VALUE).forEach(([key, value]) => {
dispatchFields({
type: 'UPDATE',
path: key,
value: career[value],
})
})
await saveAsDraft()
}
return (/* ... */)
}
export default Field
removed some code to be able to fit in one message, if you have further questions feel free to ask
the
saveAsDraft
was necessary because we found a strange bug in lexical where it does not updates the ui when you give some data to it using
dispatchFields
thanks! will see if i can figure out something for my own solution with this
the key thing here is the custom field and the
dispatchFields
payload hook. those can save you in situations where you would need to mass update fields
Star
Discord
online
Get dedicated engineering support directly from the Payload team.