Skip to content

Commit

Permalink
feat(cdk:forms): the disabled of ValidatorOptions support function (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
danranVm committed Jan 10, 2023
1 parent 0dd030d commit d633174
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 48 deletions.
34 changes: 34 additions & 0 deletions packages/cdk/forms/__tests__/abstractControl.spec.ts
Expand Up @@ -330,5 +330,39 @@ describe('abstractControl.ts', () => {

expect(control.hasError('async')).toEqual(true)
})

test('disabled work', async () => {
control = new Control({ validators: Validators.required, disabled: true })

expect(await control.validate()).toEqual(undefined)

control.enable()

expect(await control.validate()).toEqual({ required: { message: zhCNMessages.required({}, control) } })

control = new Control({
validators: [Validators.required, Validators.maxLength(5)],
disabled: (control, initializing) => (initializing ? false : control.valueRef.value === 'disabled'),
})

expect(await control.validate()).toEqual({ required: { message: zhCNMessages.required({}, control) } })

control.setValue('disable')
await flushPromises()

expect(await control.validate()).toEqual({
maxLength: {
actual: 7,
isArray: false,
maxLength: 5,
message: zhCNMessages.maxLength({ actual: 7, isArray: false, maxLength: 5 }, control),
},
})

control.setValue('disabled')
await flushPromises()

expect(await control.validate()).toEqual(undefined)
})
})
})
12 changes: 6 additions & 6 deletions packages/cdk/forms/demo/Basic.vue
Expand Up @@ -3,12 +3,12 @@
Name: <CustomInput control="name" /> <br />
Age: <CustomInput control="age" /> <br />
Email: <CustomInput control="email" /> <br />
City: <CustomInput control="address.city" /> <br />
Street: <CustomInput control="address.street" /> <br />
Zip: <CustomInput control="address.zip" /> <br />
Remark-0: <CustomInput control="remarks.0" /> <br />
Remark-1:<CustomInput control="remarks.1" /> <br />
Remark-2:<CustomInput control="remarks.2" /> <br />
City: <CustomInput :control="['address', 'city']" /> <br />
Street: <CustomInput :control="['address', 'street']" /> <br />
Zip: <CustomInput :control="['address', 'zip']" /> <br />
Remark-0: <CustomInput :control="['remarks', 0]" /> <br />
Remark-1:<CustomInput :control="['remarks', 1]" /> <br />
Remark-2:<CustomInput :control="['remarks', 2]" /> <br />

<IxButton mode="primary" @click="onSubmit">Submit</IxButton>
</CustomForm>
Expand Down
4 changes: 2 additions & 2 deletions packages/cdk/forms/docs/Api.zh.md
Expand Up @@ -81,7 +81,7 @@ export function useFormControl<T>(

| 名称 | 说明 | 类型 | 默认值 | 备注 |
| --- | --- | --- | --- | --- |
| `disabled` | 默认禁用当前控件 | `boolean` | - | - |
| `disabled` | 默认禁用当前控件 | `boolean \| (control: AbstractControl, initializing: boolean) => boolean` | - | `initializing``true` 时,表示处于初始化中,此时 `control` 的部分属性还不能访问。 |
| `name` | 控件的名称 | `string` | - | 通常用于自定义提示信息 |
| `example` | 控件的示例 | `string` | - | 通常用于自定义提示信息 |
| `trigger` | 验证器触发的时机 | `'change' \| 'blur' \| 'submit'` | `change` | - |
Expand Down Expand Up @@ -380,7 +380,7 @@ export abstract class AbstractControl<T = any> {
/**
* 获取给定控件名称或路径的子控件
*
* @param path 子控件的路径,可以是字符串或者数字,也可以是由 `.` 分割的字符串,还可以是一个数组
* @param path 子控件的路径,可以是字符串或者数字,也可以是一个数组
*/
get<K extends OptionalKeys<T>>(path: K): AbstractControl<T[K]> | undefined;
get<K extends keyof T>(path: K): AbstractControl<T[K]>;
Expand Down
69 changes: 46 additions & 23 deletions packages/cdk/forms/src/models/abstractControl.ts
Expand Up @@ -15,12 +15,14 @@ import {
type WatchOptions,
type WatchStopHandle,
computed,
nextTick,
ref,
shallowRef,
watch,
watchEffect,
} from 'vue'

import { isArray, isNil, isPlainObject, isString } from 'lodash-es'
import { isArray, isFunction, isNil, isPlainObject, isString } from 'lodash-es'

import { convertArray } from '@idux/cdk/utils'

Expand Down Expand Up @@ -162,8 +164,10 @@ export abstract class AbstractControl<T = any> {
protected _controlsStatus!: Ref<ValidateStatus>
protected _errors!: ShallowRef<ValidateErrors | undefined>
protected _disabled!: Ref<boolean>
protected _disabledFn?: (control: AbstractControl, initializing: boolean) => boolean
protected _blurred = ref(false)
protected _dirty = ref(false)
protected _initializing = ref(true)

private _validators: ValidatorFn | ValidatorFn[] | undefined
private _composedValidators: ValidatorFn | undefined
Expand Down Expand Up @@ -542,44 +546,63 @@ export abstract class AbstractControl<T = any> {
validatorOrOptions?: ValidatorFn | ValidatorFn[] | ValidatorOptions,
asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[],
) {
let disabled = false
let _disabled = false
if (isOptions(validatorOrOptions)) {
this.name = validatorOrOptions.name
this.example = validatorOrOptions.example
this._trigger = validatorOrOptions.trigger ?? this._trigger
this.setValidators(validatorOrOptions.validators)
this.setAsyncValidators(validatorOrOptions.asyncValidators)
if (validatorOrOptions.disabled) {
disabled = true
this._forEachControls(control => control.disable())
const { name, example, trigger, validators, asyncValidators, disabled } = validatorOrOptions
this.name = name
this.example = example
this.setValidators(validators)
this.setAsyncValidators(asyncValidators)

if (trigger) {
this._trigger = trigger
}

if (disabled) {
if (isFunction(disabled)) {
_disabled = disabled(this, true)
this._disabledFn = disabled
} else {
_disabled = true
}
_disabled && this._forEachControls(control => control.disable())
}
} else {
this.setValidators(validatorOrOptions)
this.setAsyncValidators(asyncValidator)
}
this._disabled = ref(disabled)
this._disabled = ref(_disabled)
}

private _init(): void {
;(this as any).controls = computed(() => this._controls.value)
;(this as any).valueRef = computed(() => this._valueRef.value)
const current = this as any
current.controls = computed(() => this._controls.value)
current.valueRef = computed(() => this._valueRef.value)
this._initErrorsAndStatus()
;(this as any).errors = computed(() => this._errors.value)
;(this as any).status = computed(() => {
current.errors = computed(() => this._errors.value)
current.status = computed(() => {
const selfStatus = this._status.value
if (selfStatus === 'valid') {
return this._controlsStatus.value
}
return selfStatus
})
;(this as any).valid = computed(() => this.status.value === 'valid')
;(this as any).invalid = computed(() => this.status.value === 'invalid')
;(this as any).validating = computed(() => this.status.value === 'validating')
;(this as any).disabled = computed(() => this._disabled.value)
;(this as any).blurred = computed(() => this._blurred.value)
;(this as any).unblurred = computed(() => !this._blurred.value)
;(this as any).dirty = computed(() => this._dirty.value)
;(this as any).pristine = computed(() => !this._dirty.value)
current.valid = computed(() => this.status.value === 'valid')
current.invalid = computed(() => this.status.value === 'invalid')
current.validating = computed(() => this.status.value === 'validating')
current.disabled = computed(() => this._disabled.value)
current.blurred = computed(() => this._blurred.value)
current.unblurred = computed(() => !this._blurred.value)
current.dirty = computed(() => this._dirty.value)
current.pristine = computed(() => !this._dirty.value)

if (this._disabledFn) {
nextTick(() => {
watchEffect(() => {
this._disabled.value = this._disabledFn!(this, false)
})
})
}
}

private _initErrorsAndStatus() {
Expand Down
2 changes: 1 addition & 1 deletion packages/cdk/forms/src/types.ts
Expand Up @@ -35,7 +35,7 @@ export interface AsyncValidatorFn {
}

export interface ValidatorOptions {
disabled?: boolean
disabled?: boolean | ((control: AbstractControl, initializing: boolean) => boolean)
name?: string
example?: string
trigger?: 'change' | 'blur' | 'submit'
Expand Down
29 changes: 17 additions & 12 deletions packages/components/form/demo/Register.vue
Expand Up @@ -37,6 +37,9 @@
<IxFormItem label="Website" labelFor="website">
<IxInput id="website" control="website"> </IxInput>
</IxFormItem>
<IxFormItem label="time" labelFor="time" required>
<IxTimePicker control="time"></IxTimePicker>
</IxFormItem>
<IxFormItem
label="Captcha"
labelFor="captcha"
Expand All @@ -53,9 +56,6 @@
</IxCol>
</IxRow>
</IxFormItem>
<IxFormItem label="time" labelFor="time" required>
<IxTimePicker control="time"></IxTimePicker>
</IxFormItem>
<IxFormItem control="agree" :controlCol="noLabelControlCol">
<IxCheckbox control="agree">I have read the <a>agreement</a> </IxCheckbox>
</IxFormItem>
Expand Down Expand Up @@ -86,27 +86,32 @@ const { required, email } = Validators
const formGroup = useFormGroup({
email: ['', [required, email]],
password: ['', required],
confirmPassword: ['', [required, confirmPasswordValidator]],
confirmPassword: [
'',
{
validators: [required, confirmPasswordValidator],
disabled: (control, initializing) => (initializing ? true : control.root.get('password')!.invalid.value),
},
],
nickname: ['', required],
phoneNumberPrefix: ['+86', required],
phoneNumber: ['', required],
website: [''],
time: [Date.now(), required],
captcha: ['', { validators: [required], disabled: true }],
captcha: [
'',
{
validators: [required],
disabled: (control, initializing) => (initializing ? true : !control.root.get('agree')!.valueRef.value),
},
],
agree: [false],
})
const passwordControl = formGroup.get('password')
const confirmPasswordControl = formGroup.get('confirmPassword')
passwordControl.watchValue(() => confirmPasswordControl.validate())
const captchaControl = formGroup.get('captcha')
const agreeControl = formGroup.get('agree')
agreeControl.watchValue(value => {
value ? captchaControl.enable() : captchaControl.disable()
})
const register = () => {
if (formGroup.valid.value) {
console.log('register', formGroup.getValue())
Expand Down
4 changes: 2 additions & 2 deletions packages/components/menu/style/themes/default.variable.less
Expand Up @@ -27,8 +27,8 @@

// dark
@menu-dark-color: var(--ix-text-color-inverse);
@menu-dark-color-hover: var(--ix-color-inverse);
@menu-dark-color-active: var(--ix-color-inverse);
@menu-dark-color-hover: var(--ix-text-color-inverse);
@menu-dark-color-active: var(--ix-text-color-inverse);
@menu-dark-color-disabled: var(--ix-text-color-inverse-disabled);
@menu-dark-background-color: var(--ix-background-color-inverse);
@menu-dark-background-color-hover: var(--ix-text-color-title-secondary);
Expand Down
2 changes: 0 additions & 2 deletions packages/components/menu/style/themes/seer.variable.less
Expand Up @@ -10,8 +10,6 @@
@menu-overlay-border-radius: var(--ix-border-radius-sm);

// dark
@menu-dark-color-hover: var(--ix-text-color-inverse);
@menu-dark-color-active: var(--ix-text-color-inverse);
@menu-dark-background-color-hover: @color-graphite-d50;
@menu-dark-background-color-active: var(--ix-color-primary);
@menu-dark-horizontal-background-color-active: @color-graphite-d30;
Expand Down

0 comments on commit d633174

Please sign in to comment.