Skip to content

Commit 6dea111

Browse files
authored
feat: expose req to defaultValue function arguments (#9937)
Rework of #5912 ### What? Now, when `defaultValue` is defined as function you can receive the `req` argument: ```ts { name: 'defaultValueFromReq', type: 'text', defaultValue: async ({ req, user, locale }) => { return Promise.resolve(req.context.defaultValue) }, }, ``` `user` and `locale` even though are repeated in `req`, this potentially leaves some room to add more args in the future without removing them now. This also improves type for `defaultValue`: ```ts type SerializableValue = boolean | number | object | string export type DefaultValue = | ((args: { locale?: TypedLocale req: PayloadRequest user: PayloadRequest['user'] }) => SerializableValue) | SerializableValue ``` ### Why? To access the current URL / search params / Local API and other things directly in `defaultValue`. ### How? Passes `req` through everywhere where we call `defaultValue()`
1 parent 26a10ed commit 6dea111

File tree

13 files changed

+64
-12
lines changed

13 files changed

+64
-12
lines changed

docs/fields/overview.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ Functions can be written to make use of the following argument properties:
212212

213213
- `user` - the authenticated user object
214214
- `locale` - the currently selected locale string
215+
- `req` - the `PayloadRequest` object
215216

216217
Here is an example of a `defaultValue` function:
217218

@@ -227,15 +228,15 @@ export const myField: Field = {
227228
name: 'attribution',
228229
type: 'text',
229230
// highlight-start
230-
defaultValue: ({ user, locale }) =>
231+
defaultValue: ({ user, locale, req }) =>
231232
`${translation[locale]} ${user.name}`,
232233
// highlight-end
233234
}
234235
```
235236

236237
<Banner type="success">
237238
<strong>Tip:</strong>
238-
You can use async `defaultValue` functions to fill fields with data from API requests.
239+
You can use async `defaultValue` functions to fill fields with data from API requests or Local API using `req.payload`.
239240
</Banner>
240241

241242
### Validation

packages/payload/src/fields/config/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ import type {
127127
TextareaFieldValidation,
128128
} from '../../index.js'
129129
import type { DocumentPreferences } from '../../preferences/types.js'
130-
import type { Operation, PayloadRequest, Where } from '../../types/index.js'
130+
import type { DefaultValue, Operation, PayloadRequest, Where } from '../../types/index.js'
131131
import type {
132132
NumberFieldManyValidation,
133133
NumberFieldSingleValidation,
@@ -395,7 +395,7 @@ export interface FieldBase {
395395
admin?: Admin
396396
/** Extension point to add your custom data. Server only. */
397397
custom?: Record<string, any>
398-
defaultValue?: any
398+
defaultValue?: DefaultValue
399399
hidden?: boolean
400400
hooks?: {
401401
afterChange?: FieldHook[]
@@ -1338,7 +1338,7 @@ export type BlocksField = {
13381338
isSortable?: boolean
13391339
} & Admin
13401340
blocks: Block[]
1341-
defaultValue?: unknown
1341+
defaultValue?: DefaultValue
13421342
labels?: Labels
13431343
maxRows?: number
13441344
minRows?: number

packages/payload/src/fields/getDefaultValue.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import type { JsonValue, PayloadRequest } from '../types/index.js'
1+
import type { DefaultValue, JsonValue, PayloadRequest } from '../types/index.js'
22

33
import { deepCopyObjectSimple } from '../utilities/deepCopyObject.js'
44

55
type Args = {
6-
defaultValue: ((args: any) => JsonValue) | any
6+
defaultValue: DefaultValue
77
locale: string | undefined
8+
req: PayloadRequest
89
user: PayloadRequest['user']
910
value?: JsonValue
1011
}
1112

1213
export const getDefaultValue = async ({
1314
defaultValue,
1415
locale,
16+
req,
1517
user,
1618
value,
1719
}: Args): Promise<JsonValue> => {
@@ -20,7 +22,7 @@ export const getDefaultValue = async ({
2022
}
2123

2224
if (defaultValue && typeof defaultValue === 'function') {
23-
return await defaultValue({ locale, user })
25+
return await defaultValue({ locale, req, user })
2426
}
2527

2628
if (typeof defaultValue === 'object') {

packages/payload/src/fields/hooks/afterRead/promise.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ export const promise = async ({
322322
siblingDoc[field.name] = await getDefaultValue({
323323
defaultValue: field.defaultValue,
324324
locale,
325+
req,
325326
user: req.user,
326327
value: siblingDoc[field.name],
327328
})

packages/payload/src/fields/hooks/beforeValidate/promise.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ export const promise = async <T>({
316316
siblingData[field.name] = await getDefaultValue({
317317
defaultValue: field.defaultValue,
318318
locale: req.locale,
319+
req,
319320
user: req.user,
320321
value: siblingData[field.name],
321322
})

packages/payload/src/types/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ export type Where = {
121121

122122
export type Sort = Array<string> | string
123123

124+
type SerializableValue = boolean | number | object | string
125+
export type DefaultValue =
126+
| ((args: {
127+
locale?: TypedLocale
128+
req: PayloadRequest
129+
user: PayloadRequest['user']
130+
}) => SerializableValue)
131+
| SerializableValue
132+
124133
/**
125134
* Applies pagination for join fields for including collection relationships
126135
*/

packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Data, Field as FieldSchema, User } from 'payload'
1+
import type { Data, Field as FieldSchema, PayloadRequest, User } from 'payload'
22

33
import { iterateFields } from './iterateFields.js'
44

@@ -7,6 +7,7 @@ type Args = {
77
fields: FieldSchema[]
88
id?: number | string
99
locale: string | undefined
10+
req: PayloadRequest
1011
siblingData: Data
1112
user: User
1213
}
@@ -16,13 +17,15 @@ export const calculateDefaultValues = async ({
1617
data,
1718
fields,
1819
locale,
20+
req,
1921
user,
2022
}: Args): Promise<Data> => {
2123
await iterateFields({
2224
id,
2325
data,
2426
fields,
2527
locale,
28+
req,
2629
siblingData: data,
2730
user,
2831
})

packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/iterateFields.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Data, Field, TabAsField, User } from 'payload'
1+
import type { Data, Field, PayloadRequest, TabAsField, User } from 'payload'
22

33
import { defaultValuePromise } from './promise.js'
44

@@ -7,6 +7,7 @@ type Args<T> = {
77
fields: (Field | TabAsField)[]
88
id?: number | string
99
locale: string | undefined
10+
req: PayloadRequest
1011
siblingData: Data
1112
user: User
1213
}
@@ -16,6 +17,7 @@ export const iterateFields = async <T>({
1617
data,
1718
fields,
1819
locale,
20+
req,
1921
siblingData,
2022
user,
2123
}: Args<T>): Promise<void> => {
@@ -28,6 +30,7 @@ export const iterateFields = async <T>({
2830
data,
2931
field,
3032
locale,
33+
req,
3134
siblingData,
3235
user,
3336
}),

packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/promise.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Data, Field, TabAsField, User } from 'payload'
1+
import type { Data, Field, PayloadRequest, TabAsField, User } from 'payload'
22

33
import { getDefaultValue } from 'payload'
44
import { fieldAffectsData, tabHasName } from 'payload/shared'
@@ -10,6 +10,7 @@ type Args<T> = {
1010
field: Field | TabAsField
1111
id?: number | string
1212
locale: string | undefined
13+
req: PayloadRequest
1314
siblingData: Data
1415
user: User
1516
}
@@ -20,6 +21,7 @@ export const defaultValuePromise = async <T>({
2021
data,
2122
field,
2223
locale,
24+
req,
2325
siblingData,
2426
user,
2527
}: Args<T>): Promise<void> => {
@@ -31,6 +33,7 @@ export const defaultValuePromise = async <T>({
3133
siblingData[field.name] = await getDefaultValue({
3234
defaultValue: field.defaultValue,
3335
locale,
36+
req,
3437
user,
3538
value: siblingData[field.name],
3639
})
@@ -52,6 +55,7 @@ export const defaultValuePromise = async <T>({
5255
data,
5356
fields: field.fields,
5457
locale,
58+
req,
5559
siblingData: row,
5660
user,
5761
}),
@@ -81,6 +85,7 @@ export const defaultValuePromise = async <T>({
8185
data,
8286
fields: block.fields,
8387
locale,
88+
req,
8489
siblingData: row,
8590
user,
8691
}),
@@ -94,13 +99,13 @@ export const defaultValuePromise = async <T>({
9499
}
95100

96101
case 'collapsible':
97-
98102
case 'row': {
99103
await iterateFields({
100104
id,
101105
data,
102106
fields: field.fields,
103107
locale,
108+
req,
104109
siblingData,
105110
user,
106111
})
@@ -119,6 +124,7 @@ export const defaultValuePromise = async <T>({
119124
data,
120125
fields: field.fields,
121126
locale,
127+
req,
122128
siblingData: groupData,
123129
user,
124130
})
@@ -143,6 +149,7 @@ export const defaultValuePromise = async <T>({
143149
data,
144150
fields: field.fields,
145151
locale,
152+
req,
146153
siblingData: tabSiblingData,
147154
user,
148155
})
@@ -156,6 +163,7 @@ export const defaultValuePromise = async <T>({
156163
data,
157164
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
158165
locale,
166+
req,
159167
siblingData,
160168
user,
161169
})

packages/ui/src/forms/fieldSchemasToFormState/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const fieldSchemasToFormState = async (args: Args): Promise<FormState> =>
8484
data: dataWithDefaultValues,
8585
fields,
8686
locale: req.locale,
87+
req,
8788
siblingData: dataWithDefaultValues,
8889
user: req.user,
8990
})

test/fields/collections/Text/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ const TextFields: CollectionConfig = {
151151
hasMany: true,
152152
maxRows: 4,
153153
},
154+
{
155+
name: 'defaultValueFromReq',
156+
type: 'text',
157+
defaultValue: async ({ req }) => {
158+
return Promise.resolve(req.context.defaultValue)
159+
},
160+
},
154161
{
155162
name: 'disableListColumnText',
156163
type: 'text',

test/fields/int.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ describe('Fields', () => {
9797
expect(fieldWithDefaultValue).toEqual(dependentOnFieldWithDefaultValue)
9898
})
9999

100+
it('should populate function default values from req', async () => {
101+
const text = await payload.create({
102+
req: {
103+
context: {
104+
defaultValue: 'from-context',
105+
},
106+
},
107+
collection: 'text-fields',
108+
data: { text: 'required' },
109+
})
110+
111+
expect(text.defaultValueFromReq).toBe('from-context')
112+
})
113+
100114
it('should localize an array of strings using hasMany', async () => {
101115
const localizedHasMany = ['hello', 'world']
102116
const { id } = await payload.create({

test/fields/payload-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ export interface TextField {
783783
localizedHasMany?: string[] | null;
784784
withMinRows?: string[] | null;
785785
withMaxRows?: string[] | null;
786+
defaultValueFromReq?: string | null;
786787
disableListColumnText?: string | null;
787788
disableListFilterText?: string | null;
788789
array?:
@@ -2997,6 +2998,7 @@ export interface TextFieldsSelect<T extends boolean = true> {
29972998
localizedHasMany?: T;
29982999
withMinRows?: T;
29993000
withMaxRows?: T;
3001+
defaultValueFromReq?: T;
30003002
disableListColumnText?: T;
30013003
disableListFilterText?: T;
30023004
array?:

0 commit comments

Comments
 (0)