angular validator and async validator

Angular Form Validation

  • 在 form.js 中参考这个方法,validator/asyncValidator 都被封装了一层。把多个 validator 方法合并成一个,然后通过forkjoin,将多个异步合并成一个,这个地方有个坑,asyncValidator 返回的虽然是AsyncValidatorFn|AsyncValidatorFn[],但是它是假设这个函数返回的是 Promise/Observable,Promise 比较简单,Observable 则必须是 compeleted 状态。jorkjoin 相当于 Promise.all(), 如果你的 Observables, 是来自于一个 Subject(Rxjs),记住一定要把它变成 completed 状态。如何变,可以采用 pipe(take(1)), 或者直接 subject.complete(), 否则,asyncValidator 不会拿到 errors. 切记切记。
export declare interface AsyncValidatorFn {
  (control: AbstractControl):
    | Promise<ValidationErrors | null>
    | Observable<ValidationErrors | null>;
}

下面的函数该怎么看呢,首先是 setUpControl, 它会将 validator/asyncValidator 封装,可以递归方法下去,方法都在下面清单里面。
当我们的 formControl 的值更新了,那么会首先调用 updateValueAndValidity(),

  • 这个方法里面先取消之前未完成的异步,this._cancelExistingSubscription();,
  • 然后执行同步的 validaiton, this._runValidator(),这个里面的this.validator 就是static compose(validators)的返回值,this就是 control
  • 然后计算 validaiton status, 只有同步的 validation 通过了,或者 status===PENDING,才会进行异步的 validaiton 验证。节省资源。
if (this.status === VALID || this.status === PENDING) {
  this._runAsyncValidator(opts.emitEvent);
}
function setUpControl(control, dir) {
  //xxx 省略若干
  control.validator = Validators.compose([
    /** @type {?} */ (control.validator),
    dir.validator,
  ]);
  control.asyncValidator = Validators.composeAsync([
    /** @type {?} */ (control.asyncValidator),
    dir.asyncValidator,
  ]);
  //xxx 省略若干
}
static compose(validators) {
        if (!validators)
            return null;
        /** @type {?} */
        const presentValidators = (/** @type {?} */ (validators.filter(isPresent)));
        if (presentValidators.length == 0)
            return null;
        return (/**
         * @param {?} control
         * @return {?}
         */
        function (control) {
            return _mergeErrors(_executeValidators(control, presentValidators));
        });
    }
static composeAsync(validators) {
        if (!validators)
            return null;
        /** @type {?} */
        const presentValidators = (/** @type {?} */ (validators.filter(isPresent)));
        if (presentValidators.length == 0)
            return null;
        return (/**
         * @param {?} control
         * @return {?}
         */
        function (control) {
            /** @type {?} */
            const observables = _executeAsyncValidators(control, presentValidators).map(toObservable);
            return forkJoin(observables).pipe(map(_mergeErrors));
        });
    }

/**
 * @param {?} control
 * @param {?} validators
 * @return {?}
 */
function _executeValidators(control, validators) {
    return validators.map((/**
     * @param {?} v
     * @return {?}
     */
    v => v(control)));
}
/**
 * @param {?} control
 * @param {?} validators
 * @return {?}
 */
function _executeAsyncValidators(control, validators) {
    return validators.map((/**
     * @param {?} v
     * @return {?}
     */
    v => v(control)));
}

/**
 * @param {?} arrayOfErrors
 * @return {?}
 */
function _mergeErrors(arrayOfErrors) {
    /** @type {?} */
    let res = {};
    // Not using Array.reduce here due to a Chrome 80 bug
    // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
    arrayOfErrors.forEach((/**
     * @param {?} errors
     * @return {?}
     */
    (errors) => {
        res = errors != null ? Object.assign(Object.assign({}, (/** @type {?} */ (res))), errors) : (/** @type {?} */ (res));
    }));
    return Object.keys(res).length === 0 ? null : res;
}

 _runValidator() {
        return this.validator ? this.validator(this) : null;
    }
    /**
     * @private
     * @param {?=} emitEvent
     * @return {?}
     */
    _runAsyncValidator(emitEvent) {
        if (this.asyncValidator) {
            ((/** @type {?} */ (this))).status = PENDING;
            /** @type {?} */
            const obs = toObservable(this.asyncValidator(this));
            this._asyncValidationSubscription =
                obs.subscribe((/**
                 * @param {?} errors
                 * @return {?}
                 */
                (errors) => this.setErrors(errors, { emitEvent })));
        }
    }
    /**
     * @private
     * @return {?}
     */
    _cancelExistingSubscription() {
        if (this._asyncValidationSubscription) {
            this._asyncValidationSubscription.unsubscribe();
        }
    }

setErrors(errors, opts = {}) {
        ((/** @type {?} */ (this))).errors = errors;
        this._updateControlsErrors(opts.emitEvent !== false);
    }

updateValueAndValidity(opts = {}) {
        this._setInitialStatus();
        this._updateValue();
        if (this.enabled) {
            this._cancelExistingSubscription();
            ((/** @type {?} */ (this))).errors = this._runValidator();
            ((/** @type {?} */ (this))).status = this._calculateStatus();
            if (this.status === VALID || this.status === PENDING) {
                this._runAsyncValidator(opts.emitEvent);
            }
        }
        if (opts.emitEvent !== false) {
            ((/** @type {?} */ (this.valueChanges))).emit(this.value);
            ((/** @type {?} */ (this.statusChanges))).emit(this.status);
        }
        if (this._parent && !opts.onlySelf) {
            this._parent.updateValueAndValidity(opts);
        }
    }
posted @ 2021-08-20 17:34  kongshu  阅读(78)  评论(0编辑  收藏  举报