I'm working on an Angular 12 project and I've been told that the way I'm creating the forms for the CRUD operations is a bit overwhelming. The way I'm currently using is just like the official primeng example. Note that there are like 7-8 CRUDs that are similar, the only difference is the form fields.
Question 1
I think the guy who told me that probably meant to replace the current code with some kind of a shared component and then enumerate all form fields and display them? Reactive forms? Or maybe there is some fancy npm package?
Question 2
I was looking at how this guy did it in that open source project. Maybe I should use his approach?
Code
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { Lecturer } from '@core/types';
import { LecturerService } from './lecturer.service';
import { ConfirmationService, MessageService, PrimeNGConfig } from 'primeng/api';
import { BreadcrumbService } from '@core/services';
import { Table } from 'primeng/table';
@Component({
selector: 'app-lecturers',
templateUrl: './lecturers.component.html'
})
export class LecturersComponent 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: 'Преподаватели' }]);
}
ngOnInit(): void {
this.lecturerService
.getAllLecturers()
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((data) => {
this.lecturers = data;
this.loading = false;
});
this.primengConfig.ripple = true;
}
ngOnDestroy(): void {
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: 'Успешно',
detail: 'Данните за преподавателя са обновени',
life: 3000
});
} else {
this.lecturers.push(this.lecturer);
// update to what's in db instead
this.messageService.add({
severity: 'success',
summary: 'Успешно',
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;
}
}
<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>
CodePudding user response:
I'd suggest putting the form in a separate component - straight away that would simplify the table component
You could use Angular Reactive Forms
which under the hood manage the object that's being updated - on submit you could emit this object via an @Output
or directly trigger the CRUD operations in the form component using the appropriate services (probably a better approach)