具有骨架效果而不是加载圆的PrimeNG DataTable
PrimeNG DataTable with a skeleton effect instead of loading circle
如何使用skeleton loader? If you look at the following docs link制作DataTable(PrimeNG),我现在的加载效果是这样的。
PrimeNG 实际上自己实现了骨架效果 here,但我在那里找不到 StackBlitz 或代码片段。
查看-lecturers.component.ts
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Lecturer } from '../lecturer';
import { LecturerService } from '../lecturer.service';
import { ConfirmationService, MessageService, PrimeNGConfig } from 'primeng/api';
import { BreadcrumbService } from '@core/services';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { Table } from 'primeng/table';
@Component({
selector: 'app-view-lecturers',
templateUrl: './view-lecturers.component.html',
providers: [MessageService, ConfirmationService]
})
export class ViewLecturersComponent implements OnInit, OnDestroy {
lecturerDialog: boolean;
lecturers: Lecturer[];
lecturer: Lecturer;
selectedLecturers: Lecturer[];
submitted: boolean;
loading: boolean = true;
private componentDestroyed$ = new Subject<boolean>();
@ViewChild('dt') table: Table;
constructor(
private lecturerService: LecturerService,
private messageService: MessageService,
private confirmationService: ConfirmationService,
private breadcrumbService: BreadcrumbService,
private primengConfig: PrimeNGConfig) {
this.breadcrumbService.setItems([
{ label: 'Преподаватели' },
{ label: 'Преглед' }
]);
}
ngOnInit() {
this.lecturerService.getLecturers()
.pipe(takeUntil(this.componentDestroyed$))
.subscribe(data => {
setTimeout(() => {
this.lecturers = data;
this.loading = false;
}, 1000);
});
this.primengConfig.ripple = true;
}
ngOnDestroy() {
this.componentDestroyed$.next(true);
this.componentDestroyed$.complete();
}
applyFilterGlobal($event: any) {
this.table.filterGlobal(($event.target as HTMLInputElement).value, 'contains');
}
openNew() {
this.lecturer = {};
this.submitted = false;
this.lecturerDialog = true;
}
deleteSelectedLecturers() {
this.confirmationService.confirm({
message: 'Сигурни ли сте, че искате да изтриете данните за избраните преподаватели?',
header: 'Потвърждение',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Да',
rejectLabel: 'Не',
accept: () => {
this.lecturers = this.lecturers.filter(val => !this.selectedLecturers.includes(val));
this.selectedLecturers = [];
this.messageService.add({ severity: 'success', summary: 'Успешно', detail: 'Данните за преподавателите са изтрити', life: 3000 });
}
});
}
editLecturer(lecturer: Lecturer) {
this.lecturer = { ...lecturer };
this.lecturerDialog = true;
}
deleteLecturer(lecturer: Lecturer) {
this.confirmationService.confirm({
message: 'Сигурни ли сте, че искате да изтриете данните за ' + lecturer.displayName + '?',
header: 'Потвърждение',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Да',
rejectLabel: 'Не',
accept: () => {
this.lecturers = this.lecturers.filter(val => val.id !== lecturer.id);
this.lecturer = {};
this.messageService.add({ severity: 'success', summary: 'Успешно', detail: 'Данните за преподавателя са изтрити', life: 3000 });
}
});
}
hideDialog() {
this.lecturerDialog = false;
this.submitted = false;
}
saveLecturer() {
this.submitted = true;
if (this.lecturer.fullName?.trim() && this.lecturer.displayName?.trim()) {
if (this.lecturer.id) {
this.lecturers[this.findIndexById(this.lecturer.id)] = this.lecturer;
this.messageService.add({ severity: 'success', summary: 'Successful', detail: 'Данните за преподавателя са обновени', life: 3000 });
}
else {
this.lecturers.push(this.lecturer);
// update to what's in db instead
this.messageService.add({ severity: 'success', summary: 'Successful', detail: 'Преподавателят е добавен', life: 3000 });
}
this.lecturers = [...this.lecturers];
this.lecturerDialog = false;
this.lecturer = {};
}
}
findIndexById(id: number): number {
let index = -1;
for (let i = 0; i < this.lecturers.length; i++) {
if (this.lecturers[i].id === id) {
index = i;
break;
}
}
return index;
}
}
查看-lecturers.component.html
<div class="p-grid">
<div class="p-col-12">
<p-toast></p-toast>
<div class="card">
<p-toolbar styleClass="p-mb-4">
<ng-template pTemplate="left">
<button pButton pRipple label="Добавяне" icon="pi pi-plus" class="p-button-success p-mr-2 p-mb-2"
(click)="openNew()"></button>
<button pButton pRipple label="Изтриване" icon="pi pi-trash" class="p-button-danger p-mb-2"
(click)="deleteSelectedLecturers()" [disabled]="!selectedLecturers || !selectedLecturers.length"></button>
</ng-template>
</p-toolbar>
<p-table #dt [value]="lecturers" [(selection)]="selectedLecturers" dataKey="id" styleClass="p-datatable-lecturers"
[rowHover]="true" [rows]="10" [showCurrentPageReport]="true" [rowsPerPageOptions]="[10,25,50]"
[loading]="loading" [paginator]="true"
currentPageReportTemplate="Показват се от {first} до {last} от общо {totalRecords} записа"
[globalFilterFields]="['fullName','displayName']">
<ng-template pTemplate="caption">
<div class="p-d-flex p-flex-column p-flex-md-row p-jc-md-between table-header">
<h5 class="p-m-0">Преподаватели</h5>
<span class="p-input-icon-left">
<i class="pi pi-search"></i>
<input pInputText type="text" (input)="applyFilterGlobal($event)" placeholder="Търсене..." />
</span>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 3rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th pSortableColumn="fullName">Име <p-sortIcon field="fullName"></p-sortIcon>
</th>
<th pSortableColumn="displayName">Псевдоним <p-sortIcon field="displayName"></p-sortIcon>
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-lecturer>
<tr>
<td>
<p-tableCheckbox [value]="lecturer"></p-tableCheckbox>
</td>
<td>
{{lecturer.displayName}}
</td>
<td>
{{lecturer.fullName}}
</td>
<td>
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success p-mr-2"
(click)="editLecturer(lecturer)"></button>
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning"
(click)="deleteLecturer(lecturer)"></button>
</td>
</tr>
</ng-template>
<ng-template pTemplate="summary">
<div class="p-d-flex p-ai-center p-jc-between">
Общо {{lecturers ? lecturers.length : 0 }} преподаватели.
</div>
</ng-template>
</p-table>
</div>
<p-dialog [(visible)]="lecturerDialog" [style]="{width: '450px'}" header="Данни за преподавател" [modal]="true"
styleClass="p-fluid">
<ng-template pTemplate="content">
<div class="p-field">
<label for="fullName">Име</label>
<input #fullName="ngModel" id="fullName" type="text" pInputText [(ngModel)]="lecturer.fullName"
[ngClass]="{'ng-dirty': (fullName.invalid && submitted) || (fullName.dirty && fullName.invalid)}" required
autofocus />
<small class="p-error" *ngIf="(fullName.invalid && submitted) || (fullName.dirty && fullName.invalid)">Името е
задължително.</small>
</div>
<div class="p-field">
<label for="displayName">Псевдоним</label>
<input #displayName="ngModel" id="displayName" type="text" pInputText [(ngModel)]="lecturer.displayName"
[ngClass]="{'ng-dirty': (displayName.invalid && submitted) || (displayName.dirty && displayName.invalid)}"
required />
<small class="p-error" *ngIf="(displayName.invalid && submitted) || (displayName.dirty && displayName.invalid)">Псевдонимът е задължителен.</small>
</div>
</ng-template>
<ng-template pTemplate="footer">
<button pButton pRipple label="Отказ" icon="pi pi-times" class="p-button-text" (click)="hideDialog()"></button>
<button pButton pRipple label="Запази" icon="pi pi-check" class="p-button-text"
(click)="saveLecturer()"></button>
</ng-template>
</p-dialog>
<p-confirmDialog [style]="{width: '450px'}"></p-confirmDialog>
</div>
</div>
将此模板添加到您的 table。当 loading 属性为 true 时,它将在 table 内显示此模板。查看模板和 loadingbody 模板的官方文档 link
<ng-template pTemplate="loadingbody">
<tr>
<td><p-skeleton></p-skeleton></td>
</tr>
</ng-template>
如何使用skeleton loader? If you look at the following docs link制作DataTable(PrimeNG),我现在的加载效果是这样的。
PrimeNG 实际上自己实现了骨架效果 here,但我在那里找不到 StackBlitz 或代码片段。
查看-lecturers.component.ts
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Lecturer } from '../lecturer';
import { LecturerService } from '../lecturer.service';
import { ConfirmationService, MessageService, PrimeNGConfig } from 'primeng/api';
import { BreadcrumbService } from '@core/services';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { Table } from 'primeng/table';
@Component({
selector: 'app-view-lecturers',
templateUrl: './view-lecturers.component.html',
providers: [MessageService, ConfirmationService]
})
export class ViewLecturersComponent implements OnInit, OnDestroy {
lecturerDialog: boolean;
lecturers: Lecturer[];
lecturer: Lecturer;
selectedLecturers: Lecturer[];
submitted: boolean;
loading: boolean = true;
private componentDestroyed$ = new Subject<boolean>();
@ViewChild('dt') table: Table;
constructor(
private lecturerService: LecturerService,
private messageService: MessageService,
private confirmationService: ConfirmationService,
private breadcrumbService: BreadcrumbService,
private primengConfig: PrimeNGConfig) {
this.breadcrumbService.setItems([
{ label: 'Преподаватели' },
{ label: 'Преглед' }
]);
}
ngOnInit() {
this.lecturerService.getLecturers()
.pipe(takeUntil(this.componentDestroyed$))
.subscribe(data => {
setTimeout(() => {
this.lecturers = data;
this.loading = false;
}, 1000);
});
this.primengConfig.ripple = true;
}
ngOnDestroy() {
this.componentDestroyed$.next(true);
this.componentDestroyed$.complete();
}
applyFilterGlobal($event: any) {
this.table.filterGlobal(($event.target as HTMLInputElement).value, 'contains');
}
openNew() {
this.lecturer = {};
this.submitted = false;
this.lecturerDialog = true;
}
deleteSelectedLecturers() {
this.confirmationService.confirm({
message: 'Сигурни ли сте, че искате да изтриете данните за избраните преподаватели?',
header: 'Потвърждение',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Да',
rejectLabel: 'Не',
accept: () => {
this.lecturers = this.lecturers.filter(val => !this.selectedLecturers.includes(val));
this.selectedLecturers = [];
this.messageService.add({ severity: 'success', summary: 'Успешно', detail: 'Данните за преподавателите са изтрити', life: 3000 });
}
});
}
editLecturer(lecturer: Lecturer) {
this.lecturer = { ...lecturer };
this.lecturerDialog = true;
}
deleteLecturer(lecturer: Lecturer) {
this.confirmationService.confirm({
message: 'Сигурни ли сте, че искате да изтриете данните за ' + lecturer.displayName + '?',
header: 'Потвърждение',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Да',
rejectLabel: 'Не',
accept: () => {
this.lecturers = this.lecturers.filter(val => val.id !== lecturer.id);
this.lecturer = {};
this.messageService.add({ severity: 'success', summary: 'Успешно', detail: 'Данните за преподавателя са изтрити', life: 3000 });
}
});
}
hideDialog() {
this.lecturerDialog = false;
this.submitted = false;
}
saveLecturer() {
this.submitted = true;
if (this.lecturer.fullName?.trim() && this.lecturer.displayName?.trim()) {
if (this.lecturer.id) {
this.lecturers[this.findIndexById(this.lecturer.id)] = this.lecturer;
this.messageService.add({ severity: 'success', summary: 'Successful', detail: 'Данните за преподавателя са обновени', life: 3000 });
}
else {
this.lecturers.push(this.lecturer);
// update to what's in db instead
this.messageService.add({ severity: 'success', summary: 'Successful', detail: 'Преподавателят е добавен', life: 3000 });
}
this.lecturers = [...this.lecturers];
this.lecturerDialog = false;
this.lecturer = {};
}
}
findIndexById(id: number): number {
let index = -1;
for (let i = 0; i < this.lecturers.length; i++) {
if (this.lecturers[i].id === id) {
index = i;
break;
}
}
return index;
}
}
查看-lecturers.component.html
<div class="p-grid">
<div class="p-col-12">
<p-toast></p-toast>
<div class="card">
<p-toolbar styleClass="p-mb-4">
<ng-template pTemplate="left">
<button pButton pRipple label="Добавяне" icon="pi pi-plus" class="p-button-success p-mr-2 p-mb-2"
(click)="openNew()"></button>
<button pButton pRipple label="Изтриване" icon="pi pi-trash" class="p-button-danger p-mb-2"
(click)="deleteSelectedLecturers()" [disabled]="!selectedLecturers || !selectedLecturers.length"></button>
</ng-template>
</p-toolbar>
<p-table #dt [value]="lecturers" [(selection)]="selectedLecturers" dataKey="id" styleClass="p-datatable-lecturers"
[rowHover]="true" [rows]="10" [showCurrentPageReport]="true" [rowsPerPageOptions]="[10,25,50]"
[loading]="loading" [paginator]="true"
currentPageReportTemplate="Показват се от {first} до {last} от общо {totalRecords} записа"
[globalFilterFields]="['fullName','displayName']">
<ng-template pTemplate="caption">
<div class="p-d-flex p-flex-column p-flex-md-row p-jc-md-between table-header">
<h5 class="p-m-0">Преподаватели</h5>
<span class="p-input-icon-left">
<i class="pi pi-search"></i>
<input pInputText type="text" (input)="applyFilterGlobal($event)" placeholder="Търсене..." />
</span>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 3rem">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
</th>
<th pSortableColumn="fullName">Име <p-sortIcon field="fullName"></p-sortIcon>
</th>
<th pSortableColumn="displayName">Псевдоним <p-sortIcon field="displayName"></p-sortIcon>
</th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-lecturer>
<tr>
<td>
<p-tableCheckbox [value]="lecturer"></p-tableCheckbox>
</td>
<td>
{{lecturer.displayName}}
</td>
<td>
{{lecturer.fullName}}
</td>
<td>
<button pButton pRipple icon="pi pi-pencil" class="p-button-rounded p-button-success p-mr-2"
(click)="editLecturer(lecturer)"></button>
<button pButton pRipple icon="pi pi-trash" class="p-button-rounded p-button-warning"
(click)="deleteLecturer(lecturer)"></button>
</td>
</tr>
</ng-template>
<ng-template pTemplate="summary">
<div class="p-d-flex p-ai-center p-jc-between">
Общо {{lecturers ? lecturers.length : 0 }} преподаватели.
</div>
</ng-template>
</p-table>
</div>
<p-dialog [(visible)]="lecturerDialog" [style]="{width: '450px'}" header="Данни за преподавател" [modal]="true"
styleClass="p-fluid">
<ng-template pTemplate="content">
<div class="p-field">
<label for="fullName">Име</label>
<input #fullName="ngModel" id="fullName" type="text" pInputText [(ngModel)]="lecturer.fullName"
[ngClass]="{'ng-dirty': (fullName.invalid && submitted) || (fullName.dirty && fullName.invalid)}" required
autofocus />
<small class="p-error" *ngIf="(fullName.invalid && submitted) || (fullName.dirty && fullName.invalid)">Името е
задължително.</small>
</div>
<div class="p-field">
<label for="displayName">Псевдоним</label>
<input #displayName="ngModel" id="displayName" type="text" pInputText [(ngModel)]="lecturer.displayName"
[ngClass]="{'ng-dirty': (displayName.invalid && submitted) || (displayName.dirty && displayName.invalid)}"
required />
<small class="p-error" *ngIf="(displayName.invalid && submitted) || (displayName.dirty && displayName.invalid)">Псевдонимът е задължителен.</small>
</div>
</ng-template>
<ng-template pTemplate="footer">
<button pButton pRipple label="Отказ" icon="pi pi-times" class="p-button-text" (click)="hideDialog()"></button>
<button pButton pRipple label="Запази" icon="pi pi-check" class="p-button-text"
(click)="saveLecturer()"></button>
</ng-template>
</p-dialog>
<p-confirmDialog [style]="{width: '450px'}"></p-confirmDialog>
</div>
</div>
将此模板添加到您的 table。当 loading 属性为 true 时,它将在 table 内显示此模板。查看模板和 loadingbody 模板的官方文档 link
<ng-template pTemplate="loadingbody">
<tr>
<td><p-skeleton></p-skeleton></td>
</tr>
</ng-template>