For example,
If editor input
How to support auto generate slug from title field in collections
as the title of the post/article.
I want another field slug to be auto-genrated with slug
how-to-support-auto-generate-slug-from-title-field-incollectionss
A better solution for slug is to add a custom element:
// Articles:
fields: [
// ...
slug({ trackingField: 'title' }),
// ...
]
// fields/slug.ts
import { Field } from 'payload/types';
import { merge } from 'lodash';
import { getSlugInput } from './ui/SlugInput';
type Slug = (
options?: { trackingField?: string },
overrides?: Partial<Field>
) => Field;
// By dynamically building fields in code configurations are reusable and concise
export const slug: Slug = ({ trackingField = 'title' } = {}, overrides) =>
merge<Field, Partial<Field> | undefined>(
{
name: 'slug',
unique: true,
type: 'text',
admin: {
position: 'sidebar',
components: {
Field: getSlugInput({ trackingField })
}
},
},
overrides
);
// SlugInput.tsx
import { kebabCase } from 'lodash';
import { TextInput, useField, useWatchForm } from 'payload/components/forms';
import { useEffect, useRef } from 'react';
export const getSlugInput = ({ trackingField }: { trackingField: string }) => () => {
const { getDataByPath } = useWatchForm();
const { value: slugValue = '', setValue: setSlugValue } = useField<string>({ path: 'slug' });
const trackingFieldValue = getDataByPath<string>(trackingField) || '';
const prevTrackingFieldValueRef = useRef(trackingFieldValue);
const stopTrackingRef = useRef(false);
useEffect(() => {
if (!trackingField || stopTrackingRef.current) {
return;
}
if (trackingFieldValue === prevTrackingFieldValueRef.current) {
return;
}
const prevSlugValue = kebabCase(prevTrackingFieldValueRef.current);
prevTrackingFieldValueRef.current = trackingFieldValue;
if (prevSlugValue !== slugValue) {
return;
}
setSlugValue(kebabCase(trackingFieldValue));
}, [trackingFieldValue]);
return (
<div>
<TextInput
name="slug"
path="slug"
label="Slug"
description={slugValue ? `Auto generated based on ${trackingField}` : `Will be auto-generated from ${trackingField} when save`}
value={slugValue}
onChange={e => {
setSlugValue(e.target.value);
stopTrackingRef.current = true;
}}
/>
</div>
);
}
I've done a similar thing. I used the beforeChange
hook.
Given the following creator function:
function generateSlugFrom(titleField: string, slugField = 'slug'): BeforeChangeHook<Product> {
return ({ data }) => {
if (!(titleField in data)) {
return data;
}
return {
...data,
[slugField]: slugify(data[titleField])
};
};
}
I use it like so:
const Products: CollectionConfig = {
slug: 'products',
hooks: {
beforeChange: [generateSlugFrom('name')]
},
fields: [
{
name: 'name',
label: 'Name',
type: 'text'
},
{
name: 'slug',
label: 'Slug',
type: 'text'
},
]
};
With help of course, from slugify.
Great, that's exactly what I want.
Another implementation from the public demo: https://github.com/payloadcms/public-demo/blob/master/src/fields/slug.ts
what is the use case for this slug? I can't seem to wrap my head around because we can query with document id only.
It's more like auto-generate slug before writing to database.
Are you looking for sth-like slug updates in UI while inputting for name/title?
slug is generated fine. I want to use this slug to query document.
Hey @MinSomai — you can totally query via the slug field.
In REST, it'd be ?where[slug][equals]=slug-here
or similar.
For more, including how to query in GraphQL and the Local API, take a look at the documentation for querying here.
I'm dumb. totally makes sense now, thanks.
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.