Cascader
Cascader
Markup Schema Example
<script lang="ts" setup>
import type { DataField } from '@silver-formily/core'
import { createForm, onFieldReact } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { createSchemaField } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAddress(pattern) {
onFieldReact(pattern, (field: DataField) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch('/location.json')
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transformAddress(data)
field.loading = false
}),
)
})
}
const form = createForm({
effects: () => {
useAddress('address')
},
})
const { SchemaField, SchemaStringField } = createSchemaField({
components: {
FormItem,
Cascader,
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<SchemaField>
<SchemaStringField
name="address"
title="Cascader"
required
x-decorator="FormItem"
x-component="Cascader"
:x-component-props="{
style: {
width: '240px',
},
}"
/>
</SchemaField>
<Submit @submit="onSubmit">
Submit
</Submit>
</Form>
</template>JSON Schema Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { createSchemaField } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAsyncDataSource(url, transform) {
return (field) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch(url)
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transform(data)
field.loading = false
}),
)
}
}
const schema = {
type: 'object',
properties: {
cascader: {
'type': 'string',
'title': 'Cascader',
'x-decorator': 'FormItem',
'x-component': 'Cascader',
'x-component-props': {
style: {
width: '240px',
},
},
'x-reactions': [
'{{useAsyncDataSource("/location.json",transformAddress)}}',
],
},
},
}
const form = createForm()
const { SchemaField } = createSchemaField({
components: {
FormItem,
Cascader,
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<SchemaField :schema="schema" :scope="{ useAsyncDataSource, transformAddress }" />
<Submit @submit="onSubmit">
Submit
</Submit>
</Form>
</template>Template Example
<script lang="ts" setup>
import type { DataField } from '@silver-formily/core'
import { createForm, onFieldReact } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { Field } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAddress(pattern) {
onFieldReact(pattern, (field: DataField) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch('/location.json')
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transformAddress(data)
field.loading = false
}),
)
})
}
const form = createForm({
effects: () => {
useAddress('address')
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<Field
name="address"
title="Cascader"
required
:decorator="[FormItem]"
:component="[
Cascader,
{
style: {
width: '240px',
},
},
]"
/>
<Submit @submit="onSubmit">
Submit
</Submit>
</Form>
</template>API
See https://element-plus.org/en-US/component/cascader.html
Object values and property selection
Set optionAsValue: true to submit option objects. The optional optionValueKeys?: string[] strictly selects top-level properties. Omit it to return complete objects; an empty array returns an empty object, and missing properties are ignored. optionAsValue defaults to false.
Projection affects only the submitted value, never dataSource. Include the identity property (value, or the configured nodeKey / rowKey) in the allowlist. Labels are resolved from the complete data source, so the form value does not need a label.
const componentProps = {
optionAsValue: true,
optionValueKeys: ['value'],
}
// dataSource: [{ value: 1, label: 'One', extra: 'Business data' }]
// Form value: { value: 1 }With emitPath=true, values are paths of objects (nested arrays for multiple selection). With emitPath=false, values are node objects (arrays for multiple selection).
<script setup lang="ts">
import { createForm } from '@silver-formily/core'
import { Cascader, FormItem } from '@silver-formily/element-plus'
import { Field, FormConsumer, FormProvider } from '@silver-formily/vue'
const form = createForm()
const options = [
{ value: 1, label: 'Alpha', extra: 'Not stored in the form' },
{ value: 2, label: 'Beta', extra: 'Not stored in the form' },
]
</script>
<template>
<FormProvider :form="form">
<Field
name="choice"
title="精简对象值"
:decorator="[FormItem]"
:component="[Cascader, { optionAsValue: true, optionValueKeys: ['value'], clearable: true }]"
:data-source="options"
/>
<FormConsumer v-slot="{ form: currentForm }">
<pre>{{ JSON.stringify(currentForm.values, null, 2) }}</pre>
</FormConsumer>
</FormProvider>
</template>{}