Skip to content

Commit

Permalink
feat(form-item): adds renderMessage prop, closes #2525
Browse files Browse the repository at this point in the history
  • Loading branch information
07akioni committed Jun 2, 2022
1 parent 7fa0927 commit 467cc19
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- 🌟 `n-steps` adds `on-update:current` prop.
- 🌟 `n-date-picker` adds `panel` prop.
- 🌟 `n-data-table` adds `on-scroll` prop, closes [#3025](https://github.com/TuSimple/naive-ui/issues/3025).
- 🌟 `FormItemRule` adds `renderMessage` prop, closes [#2525](https://github.com/TuSimple/naive-ui/issues/2525).
- `n-scrollbar` adds `trigger` prop.
- `n-input-number` adds `button-placement` prop.
- `n-select` adds `children-field` prop, closes [#3018](https://github.com/TuSimple/naive-ui/issues/3018).
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- 🌟 `n-steps` 新增 `on-update:current` 属性
- 🌟 `n-date-picker` 新增 `panel` 属性
- 🌟 `n-data-table` 新增 `on-scroll` 属性,关闭 [#3025](https://github.com/TuSimple/naive-ui/issues/3025)
- 🌟 `FormItemRule` 新增 `renderMessage` 属性,关闭 [#2525](https://github.com/TuSimple/naive-ui/issues/2525)
- `n-scrollbar` 新增 `trigger` 属性
- `n-input-number` 新增 `button-placement` 属性
- `n-select` 新增 `children-field` 属性,关闭 [#3018](https://github.com/TuSimple/naive-ui/issues/3018)
Expand Down
49 changes: 49 additions & 0 deletions src/form/demos/enUS/i18n.demo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<markdown>
# I18n

Form's rule supports `renderMessage`. You can use it to render your message.
</markdown>

<template>
<n-space vertical>
<n-radio-group v-model:value="locale">
<n-space>
<n-radio label="English" value="English" />
<n-radio label="Chinese" value="Chinese" />
</n-space>
</n-radio-group>
<n-form :model="model" :rules="rules">
<n-form-item label="Input something to remove error" path="input">
<n-input v-model:value="model.input" />
</n-form-item>
</n-form>
</n-space>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import { FormRules } from 'naive-ui'
export default defineComponent({
setup () {
const localeRef = ref('English')
const rules: FormRules = {
input: {
required: true,
trigger: ['focus', 'input'],
renderMessage: () => {
return localeRef.value === 'Chinese' ? '中文错误' : 'English error'
}
}
}
const modelRef = ref({
input: ''
})
return {
rules,
model: modelRef,
locale: localeRef
}
}
})
</script>
16 changes: 9 additions & 7 deletions src/form/demos/enUS/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The element to collect and validate data.
inline.vue
custom-rule.vue
custom-validation.vue
i18n.vue
top.vue
left.vue
item-only.vue
Expand Down Expand Up @@ -47,13 +48,14 @@ custom-messages.vue
The follow table doesn't demostrate all props of rules. If you want to know all the usages, please see <n-a href="https://github.com/yiminghe/async-validator" target="_blank">async-validator</n-a>.
</n-alert>

| Property | Type | Description |
| --- | --- | --- |
| required | `boolean` | Is it required. |
| validator | `(rule: FormItemRule, value: any) => boolean \| Error` | Validation rule. |
| asyncValidator | `(rule: FormItemRule, value: any, callback: boolean => void) => void` | Asynchronous validation in the form of a callback. |
| trigger | `string \| Array<string>` | Trigger type. |
| message | `string` | Text to show when validation fails. |
| Property | Type | Description | Version |
| --- | --- | --- | --- |
| asyncValidator | `(rule: FormItemRule, value: any, callback: boolean => void) => void` | Asynchronous validation in the form of a callback. | |
| message | `string` | Text to show when validation fails. | |
| renderMessage | `() => VNodeChild` | Render function or message. | NEXT_VERSION |
| required | `boolean` | Is it required. | |
| trigger | `string \| Array<string>` | Trigger type. | |
| validator | `(rule: FormItemRule, value: any) => boolean \| Error` | Validation rule. | |

#### FormValidateMessages Type

Expand Down
2 changes: 1 addition & 1 deletion src/form/demos/enUS/inline.demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default defineComponent({
const message = useMessage()
return {
formRef,
size: ref('medium'),
size: ref<'small' | 'medium' | 'large'>('medium'),
formValue: ref({
user: {
name: '',
Expand Down
51 changes: 51 additions & 0 deletions src/form/demos/zhCN/i18n.demo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<markdown>
# 国际化

表单的规则支持 `renderMessage`,你可以利用它来完成验证信息的国际化。
</markdown>

<template>
<n-space vertical>
<n-radio-group v-model:value="locale">
<n-space>
<n-radio label="语言1" value="语言1" />
<n-radio label="语言2" value="语言2" />
</n-space>
</n-radio-group>
<n-form :model="model" :rules="rules">
<n-form-item label="输入点什么去掉 error" path="input">
<n-input v-model:value="model.input" />
</n-form-item>
</n-form>
</n-space>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import { FormRules } from 'naive-ui'
export default defineComponent({
setup () {
const localeRef = ref('语言1')
const rules: FormRules = {
input: {
required: true,
trigger: ['focus', 'input'],
renderMessage: () => {
return localeRef.value === '语言1'
? '抽离透传归因分析作为抓手为产品赋能'
: '方法论是组合拳达到平台化标准'
}
}
}
const modelRef = ref({
input: ''
})
return {
rules,
model: modelRef,
locale: localeRef
}
}
})
</script>
16 changes: 9 additions & 7 deletions src/form/demos/zhCN/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
inline.vue
custom-rule.vue
custom-validation.vue
i18n.vue
top.vue
left.vue
item-only.vue
Expand Down Expand Up @@ -47,13 +48,14 @@ custom-messages.vue
以下并不是规则的全部用法,如果你想了解更多的用法,请参考 <n-a href="https://github.com/yiminghe/async-validator" target="_blank">async-validator</n-a>。
</n-alert>

| 属性 | 类型 | 说明 |
| --- | --- | --- |
| required | `boolean` | 是否必填 |
| validator | `(rule: FormItemRule, value: any) => boolean \| Error` | 校验规则 |
| asyncValidator | `(rule: FormItemRule, value: any, callback: boolean => void) => void` | 异步校验,支持定义回调函数 |
| trigger | `string \| Array<string>` | 触发方式 |
| message | `string` | 校验失败时展示的信息 |
| 属性 | 类型 | 说明 | 版本 |
| --- | --- | --- | --- |
| asyncValidator | `(rule: FormItemRule, value: any, callback: boolean => void) => void` | 异步校验,支持定义回调函数 | |
| message | `string` | 校验失败时展示的信息 | |
| renderMessage | `() => VNodeChild` | 信息的渲染函数 | NEXT_VERSION |
| required | `boolean` | 是否必填 | |
| trigger | `string \| Array<string>` | 触发方式 | |
| validator | `(rule: FormItemRule, value: any) => boolean \| Error` | 校验规则 | |

### FormItem Props

Expand Down
2 changes: 1 addition & 1 deletion src/form/demos/zhCN/inline.demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default defineComponent({
const message = useMessage()
return {
formRef,
size: ref('medium'),
size: ref<'small' | 'medium' | 'large'>('medium'),
formValue: ref({
user: {
name: '',
Expand Down
32 changes: 27 additions & 5 deletions src/form/src/FormItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ export default defineComponent({
}
const { value: rules } = mergedRulesRef
const value = NForm ? get(NForm.props.model, path || '') : undefined
const messageRenderers: Record<string, () => VNodeChild> = {}
const originalMessageRendersMessage: Record<string, any> = {}
const activeRules = (
!trigger
? rules
Expand All @@ -274,7 +276,7 @@ export default defineComponent({
})
)
.filter(shouldRuleBeApplied)
.map((rule) => {
.map((rule, i) => {
const shallowClonedRule = Object.assign({}, rule)
if (shallowClonedRule.validator) {
shallowClonedRule.validator = wrapValidator(
Expand All @@ -288,6 +290,13 @@ export default defineComponent({
true
) as any
}
if (shallowClonedRule.renderMessage) {
const rendererKey = `__renderMessage__${i}`
originalMessageRendersMessage[rendererKey] =
shallowClonedRule.message
shallowClonedRule.message = rendererKey
messageRenderers[rendererKey] = shallowClonedRule.renderMessage
}
return shallowClonedRule
})
if (!activeRules.length) {
Expand All @@ -304,10 +313,23 @@ export default defineComponent({
return await new Promise((resolve) => {
void validator.validate({ [mergedPath]: value }, options, (errors) => {
if (errors?.length) {
renderExplainsRef.value = errors.map((error: ValidateError) => ({
key: error?.message || '',
render: () => error?.message || ''
}))
renderExplainsRef.value = errors.map((error: ValidateError) => {
const transformedMessage = error?.message || ''
return {
key: transformedMessage,
render: () => {
if (transformedMessage.startsWith('__renderMessage__')) {
return messageRenderers[transformedMessage]()
}
return transformedMessage
}
}
})
errors.forEach((error) => {
if (error.message?.startsWith('__renderMessage__')) {
error.message = originalMessageRendersMessage[error.message]
}
})
validationErroredRef.value = true
resolve({
valid: false,
Expand Down
2 changes: 1 addition & 1 deletion src/form/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export type FormItemRule = Omit<RuleItem, 'validator' | 'asyncValidator'> & {
trigger?: ValidationTrigger | string | Array<ValidationTrigger | string>
validator?: FormItemRuleValidator
asyncValidator?: FormItemRuleAsyncValidator
renderMessage?: (message: string) => VNodeChild
renderMessage?: () => VNodeChild
}

export interface FormItemValidateOptions {
Expand Down

0 comments on commit 467cc19

Please sign in to comment.