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

atinc / ngx-tethys / 15c1f1b4-802c-40d6-8dfc-2fedb288c650

21 May 2025 03:00AM UTC coverage: 90.244% (+0.009%) from 90.235%
15c1f1b4-802c-40d6-8dfc-2fedb288c650

push

circleci

web-flow
refactor(input): migrate to signal for input #TINFR-1478 (#3419)

5569 of 6840 branches covered (81.42%)

Branch coverage included in aggregate %.

45 of 46 new or added lines in 5 files covered. (97.83%)

8 existing lines in 3 files now uncovered.

13708 of 14521 relevant lines covered (94.4%)

899.46 hits per line

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

94.55
/src/input/input.component.ts
1
import { take } from 'rxjs/operators';
2

3
import { NgTemplateOutlet } from '@angular/common';
4
import {
5
    Component,
6
    ElementRef,
7
    forwardRef,
8
    NgZone,
9
    OnInit,
10
    TemplateRef,
11
    ViewEncapsulation,
12
    inject,
1✔
13
    input,
14
    effect,
28✔
15
    signal,
16
    output,
17
    contentChild
1✔
18
} from '@angular/core';
1✔
19
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
20
import { ThyIcon } from 'ngx-tethys/icon';
21
import { ThyAutofocusDirective } from 'ngx-tethys/shared';
22
import { ThyInputDirective, ThyInputSize } from './input.directive';
23
import { coerceBooleanProperty } from 'ngx-tethys/util';
24

1✔
25
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
26
    provide: NG_VALUE_ACCESSOR,
42✔
27
    useExisting: forwardRef(() => ThyInput),
42✔
28
    multi: true
42✔
29
};
42✔
30

42✔
31
const noop = () => {};
42✔
32

42✔
33
const password = 'password';
42✔
34

42✔
35
/**
42✔
36
 * 内部集成输入框组件,建议 thy-input-group 和 thyInput 组合使用
42✔
37
 * @name thy-input
42✔
38
 * @order 50
42✔
39
 */
42✔
40
@Component({
42✔
41
    selector: 'thy-input',
42✔
42
    templateUrl: './input.component.html',
42✔
43
    providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],
42✔
44
    encapsulation: ViewEncapsulation.None,
42✔
45
    host: {
42✔
46
        class: 'thy-input form-control',
42✔
47
        '[class.form-control-active]': 'focused()',
42✔
48
        '[class.disabled]': 'disabled()'
43✔
49
    },
50
    imports: [NgTemplateOutlet, ThyInputDirective, ThyAutofocusDirective, FormsModule, ThyIcon]
51
})
52
export class ThyInput implements ControlValueAccessor, OnInit {
42✔
53
    private ngZone = inject(NgZone);
42✔
54
    private elementRef = inject(ElementRef);
55

56
    /**
57
     * Placeholder
56✔
58
     */
59
    readonly placeholder = input('');
60

28✔
61
    /**
62
     * 输入框大小
63
     * @type 'xs' | 'sm' | 'md' | 'default' | 'lg'
28✔
64
     * @default default
65
     */
66
    readonly thySize = input<ThyInputSize>();
29✔
67

68
    /**
UNCOV
69
     * 是否自动聚焦
×
70
     */
71
    readonly thyAutofocus = input(false, { transform: coerceBooleanProperty });
72

3✔
73
    /**
3✔
74
     * 输入框类型
3✔
75
     * @type 'number' | 'input'
76
     */
77
    readonly thyType = input<string>();
1✔
78

1!
UNCOV
79
    /**
×
80
     * @deprecated please use thyType
81
     */
1✔
82
    readonly _type = input<string>(undefined, { alias: 'type' });
1✔
83

1✔
84
    /**
85
     * 输入 Label 文本
86
     */
68✔
87
    readonly thyLabelText = input<string>(undefined);
88

89
    /**
2✔
90
     * 是否只读
91
     */
1✔
92
    readonly readonly = input(false, { transform: coerceBooleanProperty });
1✔
93

94
    /**
95
     * focus 聚焦事件
96
     */
97
    readonly focus = output<Event>();
98

99
    /**
100
     * blur 失焦事件
101
     */
102
    readonly blur = output<Event>();
103

104
    /**
105
     * 后置模板
106
     */
1✔
107
    readonly appendTemplate = contentChild<TemplateRef<any>>('append');
108

109
    /**
110
     * 前置模板
111
     */
112
    readonly prependTemplate = contentChild<TemplateRef<any>>('prepend');
113

114
    public type = signal<string>(undefined);
115

116
    public value = signal('');
117

118
    public showLabel = signal(false);
119

120
    public focused = signal(false);
121

122
    public disabled = signal(false);
123

124
    private onTouchedCallback: () => void = noop;
125

126
    private onChangeCallback: (_: any) => void = noop;
127

128
    public isPasswordType = signal(false);
129

130
    constructor() {
131
        effect(() => {
132
            this.type.set(this.thyType() || this._type());
133
        });
134
    }
135

136
    ngOnInit() {
137
        this.ngZone.onStable.pipe(take(1)).subscribe(() => {
138
            this.isPasswordType.set(this.isPassword(this.type()));
139
        });
140
    }
141

142
    writeValue(value: any): void {
143
        this.value.set(value);
144
    }
145

146
    registerOnChange(fn: any): void {
147
        this.onChangeCallback = fn;
148
    }
149

150
    registerOnTouched(fn: any): void {
151
        this.onTouchedCallback = fn;
152
    }
153

154
    setDisabledState?(isDisabled: boolean): void {
155
        this.disabled.set(isDisabled);
156
    }
157

158
    onModelChange() {
159
        this.onChangeCallback(this.value());
160
    }
161

162
    onInputFocus(event: Event) {
163
        this.focused.set(true);
164
        this.showLabel.set(true);
165
        this.focus.emit(event);
166
    }
167

168
    onInputBlur(event: Event) {
169
        this.onTouchedCallback();
170
        if (this.elementRef.nativeElement.onblur) {
171
            this.elementRef.nativeElement.onblur(event);
172
        }
173
        this.focused.set(false);
174
        this.showLabel.set(false);
175
        this.blur.emit(event);
176
    }
177

178
    isPassword(value: string) {
179
        return value === password;
180
    }
181

182
    togglePasswordType() {
183
        this.type.set(this.isPassword(this.type()) ? 'text' : 'password');
184
    }
185
}
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

© 2026 Coveralls, Inc