-
Notifications
You must be signed in to change notification settings - Fork 9
[RMBWEB-2780] Support for structured validation results #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
83cd99a
8d68591
f466115
db9f109
227f425
d84c44a
90e4499
78f35e5
b2f88ba
e5afe2a
3e45f64
511d66f
b8fb666
9065ddf
5d56725
2c76ca8
a8cb353
031f8a1
ba4b45e
dbba8f5
a5ec69d
82e505b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ import * as v2 from 'formstate-x-v2' | |
| import { BaseState } from '../state' | ||
| import * as v3 from '..' | ||
| import Disposable from '../disposable' | ||
| import { isPromiseLike, normalizeError, normalizeRawError } from '../utils' | ||
| import { isPromiseLike, normalizeError } from '../utils' | ||
|
|
||
| interface IV3StateFromV2<T extends v2.ComposibleValidatable<unknown, V>, V> extends v3.IState<V> { | ||
| /** The original ([email protected]) state */ | ||
|
|
@@ -68,7 +68,7 @@ class Upgrader<T extends v2.ComposibleValidatable<unknown, V>, V> extends BaseSt | |
|
|
||
| function portV2Validators<V>(...validators: Array<v3.Validator<V>>): Array<v2.Validator<V>> { | ||
| const normalizeRet = (v: any) => ( | ||
| normalizeError(normalizeRawError(v)) | ||
| normalizeError(v) | ||
| ) | ||
| return validators.map(validator => { | ||
| return (value: V) => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,12 +2,12 @@ import { action, computed, makeObservable, observable, override, reaction } from | |
| import { FieldState } from './fieldState' | ||
| import { ValidatableState } from './state' | ||
| import { IState, ValidateStatus, ValueOf } from './types' | ||
| import { debounce, normalizeError } from './utils' | ||
| import { debounce, isPassed, normalizeError } from './utils' | ||
|
|
||
| const defaultDelay = 200 // ms | ||
|
|
||
| /** Infomation synced from original state */ | ||
| type OriginalInfo<V> = Pick<IState<V>, 'activated' | 'touched' | 'error' | 'ownError'> | ||
| type OriginalInfo<V> = Pick<IState<V>, 'activated' | 'touched' | 'error' | 'ownError' | 'hasError'> | ||
|
|
||
| /** | ||
| * The state for debounce purpose. | ||
|
|
@@ -42,7 +42,8 @@ export class DebouncedState<S extends IState<V>, V = ValueOf<S>> extends Validat | |
| activated: this.$.activated, | ||
| touched: this.$.touched, | ||
| error: this.$.error, | ||
| ownError: this.$.ownError | ||
| ownError: this.$.ownError, | ||
| hasError: this.$.hasError | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -52,7 +53,7 @@ export class DebouncedState<S extends IState<V>, V = ValueOf<S>> extends Validat | |
|
|
||
| @override override get ownError() { | ||
|
||
| if (this.disabled) return undefined | ||
| if (this._error) return normalizeError(this._error) | ||
| if (this.rawError) return normalizeError(this.rawError) | ||
| return this.original.ownError | ||
| } | ||
|
|
||
|
|
@@ -62,6 +63,11 @@ export class DebouncedState<S extends IState<V>, V = ValueOf<S>> extends Validat | |
| return this.original.error | ||
| } | ||
|
|
||
| @override override get hasError() { | ||
| if (this.disabled) return false | ||
| return !isPassed(this.rawError) || this.original.hasError | ||
| } | ||
|
|
||
| @override override get validateStatus() { | ||
| if (this.disabled) return ValidateStatus.WontValidate | ||
| if ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,17 +1,17 @@ | ||||||
| import { action, autorun, computed, makeObservable, observable, when } from 'mobx' | ||||||
| import { ValidationRawError, ValidationError, IState, Validation, ValidateResult, ValidateStatus, Validator } from './types' | ||||||
| import { ValidationResult, ValidationError, IState, Validation, ValidateResult, ValidateStatus, Validator } from './types' | ||||||
| import Disposable from './disposable' | ||||||
| import { applyValidators, isPromiseLike, normalizeRawError, normalizeError, convertEmptyStringWithWarning } from './utils' | ||||||
| import { applyValidators, isPromiseLike, isPassed, normalizeError } from './utils' | ||||||
|
|
||||||
| /** Extraction for some basic features of State */ | ||||||
| export abstract class BaseState extends Disposable implements Pick< | ||||||
| IState, 'ownError' | 'hasOwnError' | 'error' | 'hasError' | 'validateStatus' | 'validating' | 'validated' | ||||||
| > { | ||||||
| IState, 'rawError' | 'ownError' | 'hasOwnError' | 'hasError' | 'validateStatus' | 'validating' | 'validated' | ||||||
|
||||||
| > { | ||||||
|
||||||
|
|
||||||
| abstract error: ValidationError | ||||||
| abstract rawError: ValidationResult | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
是预期的
啥?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 没啥,是预期的就好 |
||||||
|
|
||||||
| @computed get hasError() { | ||||||
| return this.error !== undefined | ||||||
| return !isPassed(this.rawError) | ||||||
| } | ||||||
|
|
||||||
| abstract ownError: ValidationError | ||||||
|
|
@@ -54,9 +54,9 @@ export abstract class ValidatableState<V> extends BaseState implements IState<V> | |||||
| @observable activated = false | ||||||
|
|
||||||
| /** | ||||||
| * The original error info of validation. | ||||||
| * The original return value of validation. | ||||||
|
||||||
| * The original return value of validation. | |
| * The original validation result. |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: 这个字段名改成 validationResult 会稍好点?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
其实我觉得 setError 跟 _error 配对挺好的,现在只改了这里一个, setError 暂时也动不了,是不是很奇怪?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果我们关于这一点是一致的:这里(_error & setError)叫 validation result 比叫 error 更准确;那么我们应该把尽可能多的地方改成准确的,而让为了保持兼容而留的、不够准确的尽可能少?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
甚至可以考虑:
// 这里是反映内在逻辑的
private validationResult: ValidationResult
private setValidationResult(v: ValidationResult) {
this.validationResult = v
}
// 这里是 API 层面保持兼容的
public setError(v: ValidationResult) {
return this.setValidationResult(v)
}这样就比较清晰了
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果这么改只是内部实现有这么一个概念,我感觉最主要的是让用户知道通过 setError 设置的值,跟 validation result 是等价的。
所以我觉得,在你上述的基础上把 setValidationResult 改成 public、把 setError 标识成 deprecated 会不会更合理一点
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
把
setError标识成deprecated会不会更合理一点
我觉得不用;你现在对用户侧的概念还是“error”(raw error 也是基于 error 的概念);所以在用户的感受上,setError 跟 rawError 应该是更像配对的;如果你把 setValidationResult 暴露出去,那你也应该把 rawError 命名调整成 validationResult?
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { computed } from 'mobx' | ||
| import { computed, override } from 'mobx' | ||
| import { BaseState } from './state' | ||
| import { IState, Validator, ValueOf } from './types' | ||
|
|
||
|
|
@@ -31,6 +31,10 @@ export class TransformedState<S extends IState<$V>, V, $V = ValueOf<S>> extends | |
| return this.$.rawError | ||
| } | ||
|
|
||
| @override override get hasError() { | ||
|
||
| return this.$.hasError | ||
| } | ||
|
|
||
| @computed get error() { | ||
| return this.$.error | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,9 @@ export type ValidationResult = | |
| | false | ||
| | ValidationErrorObject | ||
|
|
||
| /** Object type validation result. */ | ||
| export type ValidationErrorObject = { message: string } | ||
|
|
||
| /** Return value of validator. */ | ||
| export type ValidatorReturned = | ||
| ValidationResult | ||
|
|
@@ -19,9 +22,7 @@ export type Validation<TValue> = { | |
| returned: ValidatorReturned // result of applying validators | ||
| } | ||
|
|
||
| export type ValidationErrorObject = { message: string } | ||
| export type ValidationError = string | undefined | ||
| export type ValidationRawError = ValidationErrorObject | ValidationError | ||
|
|
||
| export type ValidateResultWithError = { hasError: true, error: NonNullable<ValidationError> } | ||
| export type ValidateResultWithValue<T> = { hasError: false, value: T } | ||
|
|
@@ -39,8 +40,8 @@ export interface IState<V = unknown> { | |
| hasError: boolean | ||
| /** The state's own error info, regardless of child states. */ | ||
| ownError: ValidationError | ||
| /** Ihe state's own error info, includes ValidationErrorObject error type, regardless of child states. */ | ||
| rawError: ValidationRawError | ||
| /** The state's validation result, is the same as the return value of the validator, regardless of child states. */ | ||
|
||
| rawError: ValidationResult | ||
| /** If the state contains its own error info. */ | ||
| hasOwnError: boolean | ||
| /** If activated (with auto-validation). */ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: 后边有时间的时候,最好是在文档 Guide/Advanced 里补一下对应的 case(可以不在这个 PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK..