• 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

11.11
/src/form/form.directive.ts
1
import { keycodes } from 'ngx-tethys/util';
2

3
import {
4
    AfterViewInit,
5
    ContentChildren,
6
    Directive,
7
    ElementRef,
8
    HostBinding,
9
    Input,
10
    NgZone,
11
    OnDestroy,
12
    OnInit,
13
    QueryList,
14
    Renderer2,
15
    inject,
1✔
16
    input,
1✔
17
    afterRenderEffect,
1✔
18
    effect,
1✔
19
    afterNextRender
2✔
20
} from '@angular/core';
21
import { ControlContainer, NgControl, NgForm } from '@angular/forms';
22
import { useHostRenderer } from '@tethys/cdk/dom';
23

24
import { ThyFormValidatorService } from './form-validator.service';
25
import { THY_FORM_CONFIG, ThyFormConfig, ThyFormLayout, ThyFormValidatorConfig } from './form.class';
1✔
26
import { coerceBooleanProperty } from '@angular/cdk/coercion';
UNCOV
27

×
28
// 1. submit 按 Enter 键提交, Textare或包含[contenteditable]属性的元素 除外,需要按 Ctrl | Command + Enter 提交
29
// 2. alwaysSubmit 不管是哪个元素 按 Enter 键都提交
UNCOV
30
// 3. forbidSubmit  Enter 键禁止提交
×
31
// 默认 submit
32
export enum ThyEnterKeyMode {
UNCOV
33
    submit = 'submit',
×
UNCOV
34
    alwaysSubmit = 'alwaysSubmit',
×
UNCOV
35
    forbidSubmit = 'forbidSubmit'
×
UNCOV
36
}
×
UNCOV
37

×
UNCOV
38
/**
×
UNCOV
39
 * 表单
×
UNCOV
40
 * @name thyForm,[thy-form]
×
UNCOV
41
 * @order 10
×
UNCOV
42
 */
×
UNCOV
43
@Directive({ selector: '[thyForm],[thy-form]', providers: [ThyFormValidatorService], exportAs: 'thyForm', host: { class: 'thy-form' } })
×
UNCOV
44
export class ThyFormDirective implements OnInit, AfterViewInit, OnDestroy {
×
UNCOV
45
    private ngForm = inject(ControlContainer);
×
46
    private elementRef = inject(ElementRef);
UNCOV
47
    private renderer = inject(Renderer2);
×
UNCOV
48
    private ngZone = inject(NgZone);
×
UNCOV
49
    readonly validator = inject(ThyFormValidatorService);
×
UNCOV
50
    private config = inject(THY_FORM_CONFIG);
×
51

52
    private hostRenderer = useHostRenderer();
53

54
    /**
55
     * 布局,默认水平居中 horizontal,其他2种布局待开发
56
     * @type horizontal | vertical | inline
57
     * @default horizontal
58
     */
59
    readonly thyLayout = input<ThyFormLayout>(undefined);
60

61
    get isHorizontal() {
62
        return this.layout === 'horizontal';
UNCOV
63
    }
×
UNCOV
64

×
65
    private get layout() {
66
        return this.thyLayout() || this.config.layout;
67
    }
UNCOV
68

×
UNCOV
69
    /**
×
UNCOV
70
     * Enter 键提交模式。`submit`: Textarea 需要 Ctrl | Command + Enter 提交,其他元素直接 Enter 提交; `alwaysSubmit`: 不管是什么元素 Enter 都提交; `forbidSubmit`: Enter 不提交
×
UNCOV
71
     * @type submit | alwaysSubmit | forbidSubmit
×
72
     * @default submit
73
     */
74
    readonly thyEnterKeyMode = input<ThyEnterKeyMode>(undefined);
UNCOV
75

×
UNCOV
76
    /**
×
UNCOV
77
     * 表单验证规则配置项 (更多内容查看:thyFormValidatorConfig)
×
78
     */
79
    readonly thyFormValidatorConfig = input<ThyFormValidatorConfig>();
UNCOV
80

×
UNCOV
81
    @HostBinding('class.was-validated') wasValidated = false;
×
82

83
    onSubmitSuccess: ($event: any) => void;
84

UNCOV
85
    private _unsubscribe: () => void;
×
86

87
    @ContentChildren(NgControl, { descendants: true })
UNCOV
88
    public controls: QueryList<NgControl>;
×
UNCOV
89

×
90
    constructor() {
91
        effect(() => {
92
            this.updateClasses();
UNCOV
93
        });
×
UNCOV
94

×
UNCOV
95
        effect(() => {
×
UNCOV
96
            const config = this.thyFormValidatorConfig();
×
UNCOV
97
            if (config) {
×
98
                this.validator.setValidatorConfig(config);
UNCOV
99
            }
×
UNCOV
100
        });
×
UNCOV
101

×
UNCOV
102
        // TODO:: replace ngAfterViewInit with afterNextRender
×
103
        // afterNextRender(() => {
104
        //     this.validator.initialize(this.ngForm as NgForm, this.elementRef.nativeElement);
105
        //     this.validator.initializeFormControlsValidation(this.controls.toArray());
106
        //     this.controls.changes.subscribe(controls => {
UNCOV
107
        //         this.validator.initializeFormControlsValidation(this.controls.toArray());
×
UNCOV
108
        //     });
×
109
        // });
110
    }
UNCOV
111

×
UNCOV
112
    ngOnInit(): void {
×
UNCOV
113
        this.ngZone.runOutsideAngular(() => {
×
114
            this._unsubscribe = this.renderer.listen(this.elementRef.nativeElement, 'keydown', this.onKeydown.bind(this));
115
        });
116
    }
117

118
    ngAfterViewInit() {
119
        this.validator.initialize(this.ngForm as NgForm, this.elementRef.nativeElement);
120
        this.validator.initializeFormControlsValidation(this.controls.toArray());
UNCOV
121
        this.controls.changes.subscribe(controls => {
×
UNCOV
122
            this.validator.initializeFormControlsValidation(this.controls.toArray());
×
UNCOV
123
        });
×
124
    }
125

126
    submit($event: Event) {
1✔
127
        const result = this.validator.validateWithDetail($event);
1✔
128
        if (result.valid) {
129
            this.onSubmitSuccess && this.onSubmitSuccess($event);
130
        } else {
131
            const invalidElement = result.invalidControls[0].element;
132
            invalidElement.focus();
133
        }
134
    }
135

1✔
136
    updateClasses() {
137
        this.hostRenderer.updateClassByMap({ [`thy-form-${this.layout}`]: true });
138
    }
139

140
    submitRunInZone($event: any) {
141
        this.ngZone.run(() => {
142
            this.submit($event);
143
        });
144
    }
145

146
    onKeydown($event: KeyboardEvent) {
147
        const currentInput = document.activeElement;
148
        const key = $event.which || $event.keyCode;
149
        if (key === keycodes.ENTER && currentInput.tagName) {
150
            const thyEnterKeyMode = this.thyEnterKeyMode();
151
            if (!thyEnterKeyMode || thyEnterKeyMode === ThyEnterKeyMode.submit) {
152
                // TEXTAREA或包含[contenteditable]属性的元素 Ctrl + Enter 或者 Command + Enter 阻止 默认行为并提交
153
                if (currentInput.tagName === 'TEXTAREA' || coerceBooleanProperty(currentInput.getAttribute('contenteditable'))) {
154
                    if ($event.ctrlKey || $event.metaKey) {
155
                        $event.preventDefault();
156
                        this.submitRunInZone($event);
157
                    }
158
                } else {
159
                    // 不是 TEXTAREA Enter 阻止 默认行为并提交
160
                    $event.preventDefault();
161
                    this.submitRunInZone($event);
162
                }
163
            } else if (thyEnterKeyMode === ThyEnterKeyMode.alwaysSubmit) {
164
                $event.preventDefault();
165
                this.submitRunInZone($event);
166
            } else {
167
                // do nothing
168
            }
169
        }
170
    }
171

172
    ngOnDestroy() {
173
        if (this._unsubscribe) {
174
            this._unsubscribe();
175
            this._unsubscribe = null;
176
        }
177
    }
178
}
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