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

atinc / ngx-tethys / c0ef8457-a839-451f-8b72-80fd73106231

02 Apr 2024 02:27PM UTC coverage: 90.524% (-0.06%) from 90.585%
c0ef8457-a839-451f-8b72-80fd73106231

Pull #3062

circleci

minlovehua
refactor(all): use the transform attribute of @Input() instead of @InputBoolean() and @InputNumber()
Pull Request #3062: refactor(all): use the transform attribute of @input() instead of @InputBoolean() and @InputNumber()

4987 of 6108 branches covered (81.65%)

Branch coverage included in aggregate %.

217 of 223 new or added lines in 82 files covered. (97.31%)

202 existing lines in 53 files now uncovered.

12246 of 12929 relevant lines covered (94.72%)

1055.59 hits per line

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

88.69
/src/pagination/pagination.component.ts
1
import {
2
    Component,
3
    OnInit,
4
    ChangeDetectionStrategy,
5
    Input,
6
    Output,
7
    EventEmitter,
8
    ChangeDetectorRef,
9
    HostBinding,
10
    Optional,
11
    Inject,
12
    TemplateRef,
13
    booleanAttribute,
14
    numberAttribute
15
} from '@angular/core';
16
import { ThyPaginationConfigModel } from './pagination.class';
17
import { PaginationDefaultConfig, DEFAULT_RANGE_COUNT, THY_PAGINATION_CONFIG, ThyPaginationConfig } from './pagination.config';
18
import { useHostRenderer } from '@tethys/cdk/dom';
1✔
19
import { isTemplateRef } from 'ngx-tethys/util';
20
import { PaginationTotalCountFormat } from './pagination.pipe';
92✔
21
import { ThyIcon } from 'ngx-tethys/icon';
92✔
22
import { ThyOption, ThyEnterDirective } from 'ngx-tethys/shared';
13✔
23
import { FormsModule } from '@angular/forms';
24
import { ThySelect } from 'ngx-tethys/select';
25
import { NgIf, NgTemplateOutlet, NgFor } from '@angular/common';
26

78✔
27
/**
78✔
28
 * 分页组件,当数据量过多时,使用分页分解数据。
78✔
29
 * @name thy-pagination
1✔
30
 * @order 10
1✔
31
 */
1✔
32
@Component({
33
    selector: 'thy-pagination',
34
    templateUrl: './pagination.component.html',
35
    changeDetection: ChangeDetectionStrategy.OnPush,
79✔
36
    standalone: true,
79✔
37
    imports: [NgIf, NgTemplateOutlet, ThySelect, FormsModule, NgFor, ThyOption, ThyIcon, ThyEnterDirective, PaginationTotalCountFormat]
2✔
38
})
2✔
39
export class ThyPagination implements OnInit {
2✔
40
    isTemplateRef = isTemplateRef;
41
    public config: ThyPaginationConfigModel = Object.assign({}, PaginationDefaultConfig, this.paginationConfig.main);
42

43
    /**
3✔
44
     * 设置当前页,支持双向绑定
3✔
45
     * @default 1
3✔
46
     */
1✔
47
    @Input({ transform: numberAttribute })
1✔
48
    set thyPageIndex(pageIndex: number) {
1✔
49
        this.pageIndex = pageIndex;
50
        if (this.initialized) {
51
            this.setPageIndex(pageIndex);
52
        }
14✔
53
    }
54

UNCOV
55
    /**
×
56
     * 每页条目数量
57
     * @default 20
58
     */
14✔
59
    @Input({ transform: numberAttribute })
14✔
60
    set thyPageSize(pageSize: number) {
61
        this.pageSize = pageSize;
62
        this.selectPageSize = pageSize;
×
63
        if (this.initialized) {
64
            this.calculatePageCount();
65
            this.initializePages(this.pageIndex, this.pageCount);
×
66
            this.cdr.markForCheck();
×
67
        }
×
68
    }
×
69

70
    /**
71
     * 总页数 与 totalPages 二选一传入
72
     */
73
    @Input({ transform: numberAttribute })
71✔
74
    set thyTotal(total: number) {
75
        this.total = total;
76
        if (this.initialized) {
66✔
77
            this.calculatePageCount();
78
            this.setPageIndex(this.pageIndex);
79
            this.cdr.markForCheck();
80✔
80
        }
80✔
81
    }
80✔
82

80✔
83
    /**
80✔
84
     * 自定义分页页码,设置自定义分页页码后将不根据 Total 和 PageSize 来自动计算页码,完全以传入的页码为准
80✔
85
     * @type number[]
80✔
86
     */
80✔
87
    @Input()
80✔
88
    set thyCustomPages(pages: number[]) {
80✔
89
        this.customPages = pages;
80✔
90
        this.config.showTotalPageCount = false;
80✔
91
        if (this.initialized) {
80✔
92
            this.calculatePageCount();
80✔
93
            this.initializePages(this.pageIndex, this.pageCount);
80✔
94
            this.cdr.markForCheck();
80✔
95
        }
80✔
96
    }
80✔
97

80✔
98
    /**
99
     * 是否禁用
100
     */
79✔
101
    @Input({ alias: 'thyDisabled', transform: booleanAttribute }) disabled = false;
79✔
102

79✔
103
    /**
79✔
104
     * 是否显示快速跳转
105
     * @default false
106
     */
79!
107
    @Input({ alias: 'thyShowQuickJumper', transform: booleanAttribute })
79!
108
    set showQuickJumper(value: boolean) {
109
        this.config.showQuickJumper = value;
110
    }
111

101✔
112
    /**
101✔
113
     * 设置是否显示总页数信息
101✔
114
     * @default true
115
     */
101✔
116
    @Input({ alias: 'thyShowTotalPageCount', transform: booleanAttribute })
117
    set showTotalPageCount(value: boolean) {
101✔
118
        this.config.showTotalPageCount = value;
101✔
119
    }
120

121
    /**
85✔
122
     * 设置分页组件的大小
85✔
123
     * @type sm | md | lg
3✔
124
     * @default md
125
     */
126
    @Input('thySize')
82!
127
    set size(size: 'sm' | 'md' | 'lg') {
128
        this.selectSize = size;
85✔
129
        this.hostRenderer.addClass(`thy-pagination-${size}`);
130
    }
131

13✔
132
    /**
133
     * 设置最大显示数量,超出最大显示数后会自动进行分割显示
134
     * @default 9
103✔
135
     */
4✔
136
    @Input({ alias: 'thyMaxCount', transform: numberAttribute })
32✔
137
    set maxCount(value: number) {
138
        this.config.maxCount = value;
139
    }
140

141
    /**
142
     * 设置边缘显示数量
4✔
143
     * @default 2
144
     */
99✔
145
    @Input({ alias: 'thyMarginalCount', transform: numberAttribute }) marginalCount: number;
99✔
146

99✔
147
    /**
99✔
148
     * 设置中间区域显示数量
99✔
149
     * @default 7
99✔
150
     */
4✔
151
    @Input({ transform: numberAttribute })
4✔
152
    set thyRangeCount(value: number) {
153
        if (Number.isInteger(value)) {
4✔
154
            this.config.rangeCount = value;
4✔
155
            if (this.initialized) {
156
                this.setMarginalCount(value);
4✔
157
            }
1✔
158
        }
159
    }
160

4!
161
    @Input({ alias: 'thyShowSizeChanger', transform: booleanAttribute })
4✔
162
    set showSizeChanger(value: boolean) {
163
        this.config.showSizeChanger = value;
4✔
164
    }
4✔
165

166
    /**
167
     * @type number[]
4✔
168
     */
4✔
169
    @Input('thyPageSizeOptions')
4✔
170
    set pageSizeOptions(value: number[]) {
1✔
171
        this.config.pageSizeOptions = value;
172
    }
4!
UNCOV
173

×
174
    /**
175
     * 只有一页时是否隐藏分页器
4✔
176
     * @default false
18✔
177
     */
178
    @Input({ alias: 'thyHideOnSinglePage', transform: booleanAttribute }) hideOnSinglePage: boolean;
179

180
    /**
181
     * 页码改变的回调
182
     */
4✔
183
    @Output('thyPageIndexChange') pageIndexChange = new EventEmitter<number>();
184

185
    /**
95✔
186
     * 与Bootstrap pagination 兼容,后续版本会进行删除,参数保持与 bootstrap 一致
284✔
187
     */
188
    @Output('thyPageChanged') pageChanged = new EventEmitter<{ page: number }>();
189

190
    @Output('thyPageSizeChanged') pageSizeChanged = new EventEmitter<number>();
191

192
    public pages: { index?: number; text?: string; active?: boolean }[] = [];
193

99✔
194
    public pageIndex = 1;
195

196
    public pageSize: number;
5✔
197

5✔
198
    public pageCount: number;
199

200
    public customPages: number[];
5!
UNCOV
201

×
202
    public total: number;
203

5✔
204
    public range = { from: 0, to: 0 };
5✔
205

206
    public firstIndex = 1;
207

2✔
208
    public isHideOnSinglePage = false;
2!
209

2✔
210
    private initialized = false;
211

2✔
212
    private hostRenderer = useHostRenderer();
213

214
    public selectSize = 'md';
2✔
215

2✔
216
    public selectPageSize: Number = 20;
2✔
217

2✔
218
    @HostBinding('class.thy-pagination') isPaginationClass = true;
219

1✔
220
    /**
221
     * 是否显示范围和total
222
     * @default false
223
     */
1✔
224
    @HostBinding('class.thy-pagination-has-total')
225
    @Input('thyShowTotal')
226
    showTotal: boolean | TemplateRef<{ $implicit: number; range: { from: number; to: number } }> = false;
227

228
    constructor(
229
        @Optional()
230
        @Inject(THY_PAGINATION_CONFIG)
231
        private paginationConfig: ThyPaginationConfig,
232
        private cdr: ChangeDetectorRef
233
    ) {}
234

235
    ngOnInit() {
236
        this.setMarginalCount(this.config.rangeCount);
237
        this.calculatePageCount();
238
        this.setPageIndex(this.pageIndex);
239
        this.initialized = true;
240
    }
241

242
    private setMarginalCount(range: number) {
243
        if (!this.marginalCount) {
244
            this.marginalCount = range <= DEFAULT_RANGE_COUNT ? 1 : 2;
245
        }
1✔
246
    }
247

248
    private setPageIndex(pageIndex: number) {
249
        this.pageIndex = pageIndex > this.pageCount ? this.pageCount : pageIndex || 1;
250
        const toPageSize = this.pageIndex * this.pageSize;
251
        this.range = {
252
            from: (this.pageIndex - 1) * this.pageSize + 1,
253
            to: toPageSize > this.total ? this.total : toPageSize
254
        };
255
        this.initializePages(this.pageIndex, this.pageCount);
256
        this.cdr.markForCheck();
257
    }
258

259
    private calculatePageCount() {
260
        let pageCount = null;
261
        if (this.customPages && this.customPages.length > 0) {
262
            pageCount = this.customPages[this.customPages.length - 1];
263
        } else {
264
            pageCount = this.pageSize < 1 ? 1 : Math.ceil(this.total / this.pageSize);
265
        }
266
        this.pageCount = Math.max(pageCount || 0, 1);
267
    }
268

269
    private makePage(index: number, text: string, active: boolean): { index: number; text: string; active: boolean } {
270
        return { index, text, active };
271
    }
272

273
    private initializePages(pageIndex: number, pageCount: number) {
274
        if (this.customPages && this.customPages.length > 0) {
275
            this.pages = this.customPages.map(page => {
276
                return {
277
                    index: page,
278
                    text: page.toString(),
279
                    active: page === +pageIndex
280
                };
281
            });
282
            return;
283
        }
284

285
        let pages = [];
286
        const marginalCount = this.marginalCount;
287
        const rangeCount = this.config.rangeCount;
288
        const maxCount = this.config.maxCount;
289
        const isMaxSized = pageCount > maxCount;
290
        if (isMaxSized) {
291
            const beforePages = [];
292
            const afterPages = [];
293

294
            // beforePages
295
            for (let i = 1; i <= marginalCount; i++) {
296
                beforePages.push(this.makePage(i, i.toString(), i === pageIndex));
297
            }
298
            if (pageIndex - Math.ceil(rangeCount / 2) > this.firstIndex) {
299
                beforePages.push(this.makePage(pageIndex - rangeCount, '···', null));
300
            }
301

302
            // afterPages
303
            if (pageIndex + Math.ceil(rangeCount / 2) < pageCount) {
304
                afterPages.push(this.makePage(pageIndex + rangeCount, '···', null));
305
            }
306
            for (let i = pageCount - marginalCount + 1; i <= pageCount; i++) {
307
                afterPages.push(this.makePage(i, i.toString(), i === pageIndex));
308
            }
309

310
            // mainPages
311
            let start = Math.max(marginalCount + 1, pageIndex - (rangeCount - 1) / 2);
312
            let end = Math.min(pageIndex + (rangeCount - 1) / 2, pageCount - marginalCount);
313
            if (pageIndex - 1 <= marginalCount) {
314
                end = rangeCount;
315
            }
316
            if (pageCount - pageIndex <= marginalCount) {
317
                start = pageCount - rangeCount + 1;
318
            }
319

320
            for (let i = start; i <= end; i++) {
321
                pages.push({
322
                    index: i,
323
                    text: i.toString(),
324
                    active: i === +pageIndex
325
                });
326
            }
327
            pages = [...beforePages, ...pages, ...afterPages];
328
        } else {
329
            for (let i = 1; i <= pageCount; i++) {
330
                pages.push({
331
                    index: i,
332
                    text: i.toString(),
333
                    active: i === +pageIndex
334
                });
335
            }
336
        }
337
        this.pages = pages;
338
    }
339

340
    private pageChange(pageIndex: number) {
341
        this.pageIndexChange.emit(pageIndex);
342
        this.pageChanged.emit({ page: pageIndex });
343
    }
344

345
    selectPage(pageIndex: number) {
346
        if (this.disabled || pageIndex === this.firstIndex - 1 || pageIndex === this.pageCount + 1) {
347
            return;
348
        }
349
        this.setPageIndex(pageIndex);
350
        this.pageChange(this.pageIndex);
351
    }
352

353
    jumpPage(input: HTMLInputElement) {
354
        const pageIndex = +input.value;
355
        if (Number.isInteger(pageIndex)) {
356
            this.selectPage(pageIndex);
357
        }
358
        input.value = '';
359
    }
360

361
    onPageSizeChange(event: number) {
362
        this.pageSize = event;
363
        this.calculatePageCount();
364
        this.setPageIndex(this.pageIndex);
365
        this.pageSizeChanged.emit(event);
366
    }
367
}
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