• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atinc / ngx-tethys / d9ae709b-3c27-4b69-b125-b8b80b54f90b

pending completion
d9ae709b-3c27-4b69-b125-b8b80b54f90b

Pull #2757

circleci

mengshuicmq
fix: fix code review
Pull Request #2757: feat(color-picker): color-picker support disabled (#INFR-8645)

98 of 6315 branches covered (1.55%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

2392 of 13661 relevant lines covered (17.51%)

83.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.82
/src/form/form-validator.service.ts
1
import { Dictionary } from 'ngx-tethys/types';
2
import { isUndefinedOrNull } from 'ngx-tethys/util';
3
import { of, Subject } from 'rxjs';
4
import { debounceTime, distinctUntilChanged, filter, switchMap, takeUntil } from 'rxjs/operators';
5

6
import { Injectable, OnDestroy } from '@angular/core';
7
import { AbstractControl, FormControlName, FormGroupDirective, NgControl, NgForm, ValidationErrors } from '@angular/forms';
8

9
import { ERROR_VALUE_REPLACE_REGEX, ThyFormValidatorLoader } from './form-validator-loader';
10
import { ThyFormValidatorConfig, ThyValidateOn } from './form.class';
11

1✔
12
/**
13
 * @private
×
14
 */
×
15
@Injectable()
×
16
export class ThyFormValidatorService implements OnDestroy {
17
    private _ngForm: NgForm | FormGroupDirective;
18

×
19
    private _formElement: HTMLFormElement;
20

21
    private _config: ThyFormValidatorConfig;
22

×
23
    public errors: string[] = [];
×
24

×
25
    private _controls: NgControl[] = [];
×
26

27
    // 记录所有元素的验证信息
28
    public validations: Dictionary<{
29
        hasError?: boolean;
×
30
        errorMessages?: string[];
×
31
    }> = {};
×
32

33
    private _destroy$ = new Subject<void>();
×
34

35
    private _getElement(name: string) {
36
        const element = this._formElement.elements[name];
×
37
        if (element) {
38
            return element;
39
        } else {
×
40
            return this._formElement.querySelector(`[name='${name}']`);
41
        }
42
    }
×
43

44
    private _clearElementError(name: string) {
×
45
        if (this.validations[name] && this.validations[name].hasError) {
46
            this.validations[name].hasError = false;
×
47
            this.validations[name].errorMessages = [];
×
48
            this.thyFormValidateLoader.removeError(this._getElement(name));
49
        }
50
    }
51

52
    private _tryGetValidation(name: string) {
×
53
        const controls = this._getControls();
×
54
        if (!this.validations[name]) {
55
            this._initializeFormControlValidation(name, controls[name] as any);
×
56
        }
×
57
        return this.validations[name];
×
58
    }
59

60
    private _addError(message: string) {
61
        this.errors.unshift(message);
×
62
    }
×
63

64
    private _clearErrors() {
65
        this.errors = [];
66
    }
67

68
    private _setControlValidateByChange(control: NgControl) {
×
69
        control.valueChanges
70
            .pipe(
71
                debounceTime(100),
72
                distinctUntilChanged(),
×
73
                filter(item => {
×
74
                    return item;
75
                }),
76
                switchMap(item => {
×
77
                    this.validateControl(control.name as string);
×
78
                    return of([]);
79
                })
×
80
            )
×
81
            .subscribe();
×
82
    }
83

84
    private _setControlValidateByBlur(control: NgControl) {
85
        const element: HTMLElement = this._getElement(control.name as string);
86
        if (element) {
×
87
            // 继承了 AbstractControlValueAccessor 的自定义 Accessor,通过 __onBlurValidation 控制触发验证函数
×
88
            if (control.valueAccessor['__onBlurValidation']) {
×
89
                control.valueAccessor['__onBlurValidation'] = () => {
×
90
                    this.validateControl(control.name as string);
91
                };
92
            } else {
93
                element.onblur = (event: FocusEvent) => {
×
94
                    this.validateControl(control.name as string);
×
95
                };
×
96
            }
×
97
        }
×
98
    }
×
99

100
    private _initializeFormControlValidation(name: string, control: AbstractControl | FormControlName | NgControl) {
101
        this.validations[name] = {
102
            hasError: false,
103
            errorMessages: []
×
104
        };
105
        if (this._getValidateOn() === 'change') {
106
            this._setControlValidateByChange(control as NgControl);
107
        } else {
×
108
            if (this._getValidateOn() === 'blur') {
×
109
                this._setControlValidateByBlur(control as NgControl);
110
            }
111

112
            control.valueChanges.pipe(takeUntil(this._destroy$)).subscribe(item => {
×
113
                this._clearElementError(name);
114
                this._clearErrors();
115
            });
×
116
        }
117
    }
×
118

119
    private _restFormControlValidation(name: string) {
120
        const validation = this.validations[name];
×
121
        if (validation) {
×
122
            validation.hasError = false;
×
123
            validation.errorMessages = [];
×
124
        }
125
    }
126

×
127
    private _formatValidationMessage(name: string, message: string) {
128
        const controls = this._getControls();
129
        const control = controls[name];
×
130
        if (control) {
×
131
            return message.replace(ERROR_VALUE_REPLACE_REGEX, (tag, key) => {
×
132
                if (key) {
×
133
                    return isUndefinedOrNull(control.errors[key][key]) ? control.errors[key].requiredLength : control.errors[key][key];
134
                }
135
            });
×
136
        } else {
137
            return message;
138
        }
×
139
    }
×
140

×
141
    private _getValidationMessage(name: string, validationError: string) {
142
        let message = null;
×
143
        if (
×
144
            this._config &&
145
            this._config.validationMessages &&
146
            this._config.validationMessages[name] &&
×
147
            this._config.validationMessages[name][validationError]
×
148
        ) {
149
            message = this._config.validationMessages[name][validationError];
150
        } else {
×
151
            message = this.thyFormValidateLoader.getErrorMessage(name, validationError);
×
152
        }
×
153
        return this._formatValidationMessage(name, message);
×
154
    }
155

156
    private _getValidationMessages(name: string, validationErrors: ValidationErrors) {
×
157
        const messages = [];
158
        for (const validationError in validationErrors) {
159
            if (validationErrors.hasOwnProperty(validationError)) {
160
                messages.push(this._getValidationMessage(name, validationError));
×
161
            }
162
        }
163
        return messages;
×
164
    }
×
165

166
    private _setControlValidationError(name: string, errorMessages: string[]) {
×
167
        const validation = this._tryGetValidation(name);
×
168
        validation.errorMessages = errorMessages;
×
169
        validation.hasError = true;
×
170
        this.thyFormValidateLoader.showError(this._getElement(name), errorMessages);
171
    }
×
172

173
    private _getValidateOn(): ThyValidateOn {
174
        return (this._config && this._config.validateOn) || this.thyFormValidateLoader.validateOn;
175
    }
×
176

×
177
    constructor(private thyFormValidateLoader: ThyFormValidatorLoader) {}
178

179
    initialize(ngForm: NgForm | FormGroupDirective, formElement: HTMLFormElement) {
×
180
        this._ngForm = ngForm;
×
181
        this._formElement = formElement;
×
182
    }
×
183

×
184
    initializeFormControlsValidation(controls: NgControl[]) {
185
        if (this._getValidateOn() !== 'submit') {
186
            (controls || []).forEach((control: NgControl) => {
187
                if (!this._controls.find(item => item.name === control.name)) {
188
                    this._initializeFormControlValidation(control.name as string, control);
189
                }
190
            });
×
191
            this._controls = controls;
×
192
        }
×
193
    }
×
194

×
195
    setValidatorConfig(config: ThyFormValidatorConfig) {
196
        this._config = config;
197
    }
198

×
199
    private _getControls() {
×
200
        if (this._ngForm instanceof NgForm) {
×
201
            return (this._ngForm as NgForm).controls;
×
202
        } else if (this._ngForm instanceof FormGroupDirective) {
203
            const controls = {};
204
            (this._ngForm as FormGroupDirective).directives.forEach(directive => {
205
                controls[directive.name] = directive;
206
            });
×
207
            return controls;
208
        }
209
    }
×
210

×
211
    private _getControlByName(name: string): AbstractControl | FormControlName {
×
212
        const controls = this._getControls();
213
        return controls[name];
214
    }
×
215

×
216
    validateControl(name: string) {
×
217
        this._clearElementError(name);
×
218
        const control = this._getControlByName(name);
×
219
        if (control && control.invalid) {
220
            const errorMessages = this._getValidationMessages(name, control.errors);
221
            this._setControlValidationError(name, errorMessages);
222
        }
223
    }
×
224

×
225
    validateControls() {
226
        // 主要是 无法检测到 ngForm 的 controls 的变化,或者是我没有找到
227
        // 验证的时候循环 ngForm 的 controls 验证
×
228
        // 发现没有 validation 初始化一个,已经存在不会重新初始化,保存缓存数据
229
        const controls = this._getControls();
1✔
230
        for (const name in controls) {
231
            if (controls.hasOwnProperty(name)) {
232
                this._tryGetValidation(name);
233
                this.validateControl(name);
1✔
234
            }
235
        }
236
        // 移除已经不存在的 validation
237
        const names = Object.keys(this.validations);
238
        names.forEach(name => {
239
            if (!controls[name]) {
240
                delete this.validations[name];
241
            }
242
        });
243
    }
244

245
    addError(message: string) {
246
        this._addError(message);
247
    }
248

249
    validate($event?: Event): boolean {
250
        this._ngForm.onSubmit($event);
251
        this.validateControls();
252
        return this._ngForm.valid;
253
    }
254

255
    reset() {
256
        this._ngForm.reset();
257
        for (const name in this.validations) {
258
            if (this.validations.hasOwnProperty(name)) {
259
                this._restFormControlValidation(name);
260
                this._clearElementError(name);
261
            }
262
        }
263
    }
264

265
    setElementErrorMessage(name: string, message: string) {
266
        this._clearElementError(name);
267
        this._setControlValidationError(name, [message]);
268
    }
269

270
    ngOnDestroy(): void {
271
        this._destroy$.next();
272
    }
273
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc