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

atinc / ngx-tethys / #55

30 Jul 2025 07:08AM UTC coverage: 9.866% (-80.4%) from 90.297%
#55

push

why520crazy
feat(empty): add setMessage for update display text #TINFR-2616

92 of 6794 branches covered (1.35%)

Branch coverage included in aggregate %.

2014 of 14552 relevant lines covered (13.84%)

6.15 hits per line

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

5.94
/src/empty/empty.component.ts
1
import {
2
    AfterViewInit,
3
    Component,
4
    computed,
5
    linkedSignal,
6
    contentChild,
7
    effect,
8
    ElementRef,
9
    inject,
10
    input,
11
    NgZone,
12
    Signal,
13
    TemplateRef
14
} from '@angular/core';
1✔
15
import { useHostRenderer } from '@tethys/cdk/dom';
16
import { ThyTranslate } from 'ngx-tethys/core';
17

18
import { NgClass, NgTemplateOutlet } from '@angular/common';
19
import { DomSanitizer } from '@angular/platform-browser';
1✔
20
import { injectLocale, ThyEmptyLocale } from 'ngx-tethys/i18n';
21
import { ThyIcon } from 'ngx-tethys/icon';
22
import { SafeAny } from 'ngx-tethys/types';
23
import { coerceBooleanProperty } from 'ngx-tethys/util';
24
import { ThyEmptyConfig } from './empty.config';
25
import { PRESET_SVG } from './svgs';
26

27
const sizeClassMap = {
28
    lg: ['thy-empty-state', 'thy-empty-state--lg'],
29
    md: ['thy-empty-state'],
30
    sm: ['thy-empty-state', 'thy-empty-state--sm']
31
};
32

33
const sizeMap = {
34
    lg: {
35
        height: 168, // 空提示的高度
36
        offsetTop: 30, // 空提示图标和大小之间的空白距离,需要除去,否则会不居中
37
        defaultMarginTop: 120 // 不自动计算默认的 top 距离
38
    },
39
    md: {
40
        height: 118,
41
        offsetTop: 20,
1✔
42
        defaultMarginTop: 10
43
    },
×
44
    sm: {
45
        height: 78,
46
        offsetTop: 10,
×
47
        defaultMarginTop: 10
×
48
    }
×
49
};
×
50

×
51
/** https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading */
×
52
export type ThyEmptyImageLoading = 'eager' | 'lazy';
×
53

×
54
/** https://wicg.github.io/priority-hints/#idl-index */
×
55
export type ThyEmptyImageFetchPriority = 'high' | 'low' | 'auto';
×
56

×
57
/**
×
58
 * 空页面组件
×
59
 * @name thy-empty
×
60
 * @order 10
×
61
 */
×
62
@Component({
×
63
    selector: 'thy-empty',
×
64
    templateUrl: './empty.component.html',
×
65
    imports: [ThyIcon, NgClass, NgTemplateOutlet]
×
66
})
×
67
export class ThyEmpty implements AfterViewInit {
×
68
    private thyTranslate = inject(ThyTranslate);
×
69
    private thyEmptyConfig = inject(ThyEmptyConfig);
×
70
    private elementRef = inject(ElementRef);
×
71
    private ngZone = inject(NgZone);
72
    private sanitizer = inject(DomSanitizer);
×
73
    private locale: Signal<ThyEmptyLocale> = injectLocale('empty');
×
74

×
75
    /**
76
     * 显示文本提示信息。同时传入 thyMessage,thyTranslationKey,thyEntityName,thyEntityNameTranslateKey 时优先级最高
×
77
     * @default 暂无数据
×
78
     */
79
    readonly thyMessage = input<string>();
×
80

×
81
    /**
82
     * 已废弃。显示文本提示信息多语言 Key。同时传入 thyTranslationKey,thyEntityName,thyEntityNameTranslateKey 时优先级最高
83
     * @deprecated
84
     */
×
85
    readonly thyTranslationKey = input<string>();
×
86

87
    /**
88
     * 已废弃。显示文本提示信息多语言 Key 的 Values。传入 thyTranslationKey 后,传入这个才会生效
89
     * @deprecated
×
90
     */
×
91
    readonly thyTranslationValues = input<any>();
92

93
    /**
×
94
     * 已废弃。显示默认提示信息,替换默认提示信息的目标对象,比如:没有 {thyEntityName}。同时传入 thyEntityName,thyEntityNameTranslateKey 时优先级较高
95
     * @deprecated
96
     */
×
97
    readonly thyEntityName = input<string>();
×
98

99
    /**
100
     * 已废弃。thyEntityName 的多语言 Key。thyMessage,thyTranslationKey,thyEntityName 均未传入时才会生效
101
     * @deprecated
×
102
     */
×
103
    readonly thyEntityNameTranslateKey = input<string>();
×
104

105
    /**
×
106
     * 提示图标名
×
107
     */
108
    readonly thyIconName = input<string>();
×
109

110
    /**
×
111
     * 大小
×
112
     * @type sm | md | lg
113
     * @default md
×
114
     */
115
    readonly thySize = input<string>('md');
×
116

×
117
    /**
118
     * 距上距离
119
     */
120
    readonly thyMarginTop = input<number | string>();
×
121

×
122
    /**
×
123
     * 是否自动根据父容器计算高度,垂直居中
124
     * @default false
125
     */
×
126
    readonly thyTopAuto = input(false, { transform: coerceBooleanProperty });
127

128
    /**
×
129
     * 自动计算高度垂直居中(即 thyTopAuto 为 true)时,支持传入自定义父容器
×
130
     */
131
    readonly thyContainer = input<ElementRef>();
132

133
    /**
×
134
     * 提示图片链接
×
135
     */
×
136
    readonly thyImageUrl = input<string>();
137

138
    readonly thyImageLoading = input<ThyEmptyImageLoading>();
139

×
140
    readonly thyImageFetchPriority = input<ThyEmptyImageFetchPriority>();
×
141

×
142
    /**
143
     * 显示文本描述
144
     */
145
    readonly thyDescription = input<string>();
1✔
146

1✔
147
    private hostRenderer = useHostRenderer();
148

149
    /**
150
     * 除提示图片,文本外的其他信息传入模板
151
     * @type TemplateRef
152
     */
153
    readonly extraTemplateRef = contentChild<TemplateRef<SafeAny>>('extra');
154

155
    protected readonly presetSvg = computed(() => {
156
        let presetSvg = this.thyIconName() ? PRESET_SVG[this.thyIconName() as keyof typeof PRESET_SVG] : PRESET_SVG.default;
157

158
        return presetSvg ? this.sanitizer.bypassSecurityTrustHtml(presetSvg) : '';
159
    });
160

161
    protected readonly displayText = linkedSignal(() => {
162
        if (this.thyMessage()) {
163
            return this.thyMessage();
164
        } else if (this.thyTranslationKey()) {
1✔
165
            return this.thyTranslate.instant(this.thyTranslationKey(), this.thyTranslationValues());
166
        } else if (this.thyEntityName()) {
167
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultWithTargetTranslateKey, {
168
                target: this.thyEntityName()
169
            });
170
        } else if (this.thyEntityNameTranslateKey()) {
171
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultWithTargetTranslateKey, {
172
                target: this.thyTranslate.instant(this.thyEntityNameTranslateKey())
173
            });
174
        } else if (this.thyTranslate.instant(this.thyEmptyConfig.noResultTranslateKey) !== 'common.tips.NO_RESULT') {
175
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultTranslateKey);
176
        } else {
177
            return this.locale().noDataText;
178
        }
179
    });
180

181
    setMessage(text: string) {
182
        this.displayText.set(text);
183
    }
184

185
    constructor() {
186
        effect(() => {
187
            this.updateClass();
188
        });
189
    }
190

191
    private _calculatePosition() {
192
        const sizeOptions = sizeMap[(this.thySize() as keyof typeof sizeMap) || 'md'];
193
        let marginTop = null;
194
        if (this.thyTopAuto()) {
195
            // 选择参考父容器居中
196
            const thyContainer = this.thyContainer();
197
            const containerElement = thyContainer ? thyContainer.nativeElement : this.elementRef.nativeElement.parentElement;
198
            // containerElement.height;
199
            let emptyStateHeight = this.elementRef.nativeElement.offsetHeight;
200
            // 高度没有自动计算出来使用默认值
201
            if (emptyStateHeight <= 10) {
202
                emptyStateHeight = sizeOptions.height;
203
            }
204
            marginTop = (containerElement.offsetHeight - emptyStateHeight) / 2 - sizeOptions.offsetTop;
205
            // marginTop = (containerElement.offsetHeight - emptyStateHeight) / 2;
206
            if (marginTop < 0) {
207
                marginTop = 0; // sizeOptions.defaultMarginTop;
208
            }
209
        } else {
210
            const thyMarginTop = this.thyMarginTop();
211
            if (thyMarginTop) {
212
                marginTop = thyMarginTop;
213
            } else {
214
                marginTop = 0; // sizeOptions.defaultMarginTop;
215
            }
216
        }
217
        if (marginTop) {
218
            this.hostRenderer.setStyle('marginTop', marginTop + 'px');
219
        }
220
    }
221

222
    updateClass() {
223
        const classList = sizeClassMap[(this.thySize() as keyof typeof sizeClassMap) || 'md'];
224
        if (classList) {
225
            this.hostRenderer.updateClass(classList);
226
        }
227
    }
228

229
    ngAfterViewInit() {
230
        this.ngZone.runOutsideAngular(() => {
231
            setTimeout(() => {
232
                this._calculatePosition();
233
            }, 50);
234
        });
235
    }
236
}
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