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

atinc / ngx-tethys / 68ef226c-f83e-44c1-b8ed-e420a83c5d84

28 May 2025 10:31AM UTC coverage: 10.352% (-80.0%) from 90.316%
68ef226c-f83e-44c1-b8ed-e420a83c5d84

Pull #3460

circleci

pubuzhixing8
chore: xxx
Pull Request #3460: refactor(icon): migrate signal input #TINFR-1476

132 of 6823 branches covered (1.93%)

Branch coverage included in aggregate %.

10 of 14 new or added lines in 1 file covered. (71.43%)

11648 existing lines in 344 files now uncovered.

2078 of 14525 relevant lines covered (14.31%)

6.69 hits per line

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

1.16
/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, inject } 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, ThyValidateResult } from './form.class';
11

1✔
12
/**
UNCOV
13
 * @private
×
UNCOV
14
 */
×
UNCOV
15
@Injectable()
×
16
export class ThyFormValidatorService implements OnDestroy {
UNCOV
17
    private thyFormValidateLoader = inject(ThyFormValidatorLoader);
×
UNCOV
18

×
19
    private _ngForm: NgForm | FormGroupDirective;
20

UNCOV
21
    private _formElement: HTMLFormElement;
×
UNCOV
22

×
UNCOV
23
    private _config: ThyFormValidatorConfig;
×
24

25
    public errors: string[] = [];
UNCOV
26

×
27
    private _controls: NgControl[] = [];
28

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

35
    private _destroy$ = new Subject<void>();
36

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

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

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

×
62
    private _addError(message: string) {
UNCOV
63
        this.errors.unshift(message);
×
UNCOV
64
    }
×
65

×
66
    private _clearErrors() {
67
        this.errors = [];
68
    }
UNCOV
69

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

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

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

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

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

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

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

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

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

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

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

×
184
    initializeFormControlsValidation(controls: NgControl[]) {
UNCOV
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

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

×
UNCOV
199
    private _getControls() {
×
UNCOV
200
        if (this._ngForm instanceof NgForm) {
×
UNCOV
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 => {
UNCOV
205
                controls[directive.name] = directive;
×
UNCOV
206
            });
×
UNCOV
207
            return controls;
×
208
        }
×
209
    }
210

UNCOV
211
    private _getControlByName(name: string): AbstractControl | FormControlName {
×
212
        const controls = this._getControls();
213
        return controls[name];
UNCOV
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);
UNCOV
222
        }
×
UNCOV
223
        return {
×
UNCOV
224
            valid: control.valid,
×
225
            control: control,
UNCOV
226
            element: this._getElement(name)
×
UNCOV
227
        };
×
228
    }
229

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

253
    addError(message: string) {
254
        this._addError(message);
255
    }
256

257
    validate($event?: Event): boolean {
258
        this._ngForm.onSubmit($event);
259
        this.validateControls();
260
        return this._ngForm.valid;
261
    }
262

263
    validateWithDetail($event?: Event): ThyValidateResult {
264
        this._ngForm.onSubmit($event);
265
        const results = this.validateControls();
266
        return {
267
            valid: this._ngForm.valid,
268
            invalidControls: results.filter(res => !res.valid),
269
            validControls: results.filter(res => res.valid)
270
        };
271
    }
272

273
    reset() {
274
        this._ngForm.reset();
275
        for (const name in this.validations) {
276
            if (this.validations.hasOwnProperty(name)) {
277
                this._restFormControlValidation(name);
278
                this._clearElementError(name);
279
            }
280
        }
281
    }
282

283
    setElementErrorMessage(name: string, message: string) {
284
        this._clearElementError(name);
285
        this._setControlValidationError(name, [message]);
286
    }
287

288
    ngOnDestroy(): void {
289
        this._destroy$.next();
290
    }
291
}
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