Add customizable edit page

Adding the item being edited to local storage, so it doesn't have to request it again
This commit is contained in:
Norbi Peti 2022-01-30 18:13:25 +01:00
parent 79fb876da8
commit 512cbb9af6
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
17 changed files with 164 additions and 19 deletions

View file

@ -11,7 +11,7 @@ export class ApiService {
constructor(private http: HttpClient, private loginService: LoginService) {
}
request(method: 'post' | 'get' | 'delete', url: string, body: any): Promise<any> {
request(method: 'post' | 'get' | 'delete' | 'patch', url: string, body: any): Promise<any> {
return this.http.request(method, environment.backendUrl + url, {
body,
headers: {Authorization: 'Bearer ' + this.loginService.token}

View file

@ -0,0 +1,3 @@
export class Model {
id: number;
}

View file

@ -1,4 +1,6 @@
export class User {
import { Model } from './model';
export class User extends Model {
name: string;
isAdmin: boolean;
}

View file

@ -0,0 +1,12 @@
<div *ngIf="item; else loading">
<form [formGroup]="formGroup">
<mat-form-field *ngFor="let field of fields">
<mat-label>{{ field.title }}</mat-label>
<input matInput [formControlName]="field.name" type="text"/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="submit()">Mentés</button>
</form>
</div>
<ng-template #loading>
<mat-spinner [diameter]="100" [style.margin]="'auto'"></mat-spinner>
</ng-template>

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditComponent } from './edit.component';
describe('EditComponent', () => {
let component: EditComponent;
let fixture: ComponentFixture<EditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EditComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(EditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,41 @@
import { Component, Input, OnInit } from '@angular/core';
import { ApiService } from '../../api.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Model } from '../../model/model';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
export class EditComponent<T extends Model> implements OnInit {
item?: T;
@Input() apiPath: string;
@Input() fields: { title: string, name: string }[];
formGroup: FormGroup;
constructor(private api: ApiService, private router: Router, private fb: FormBuilder, private route: ActivatedRoute) {
}
async ngOnInit(): Promise<void> {
this.item = JSON.parse(window.localStorage.getItem(this.router.url));
window.localStorage.removeItem(this.router.url);
if (!this.item) {
this.item = await this.api.request('get', this.apiPath + '/' + this.route.snapshot.url[this.route.snapshot.url.length - 1], {});
}
this.formGroup = this.fb.group(this.fields.reduce((pv, cv) => Object.assign(pv, {[cv.name]: new FormControl()}), {}));
this.formGroup.patchValue(this.item);
}
async submit(): Promise<void> {
try {
await this.api.request('patch', this.apiPath + '/' + this.item.id, this.formGroup.value);
} catch (e) {
alert(e);
}
}
}

View file

@ -1,4 +1,4 @@
<app-table [items]="items" (pageChange)="handlePageChange($event)" [paginationData]="paginationData" [columns]="columns"
[loading]="loading">
[loading]="loading" (editItem)="editItem($event)">
</app-table>

View file

@ -2,23 +2,25 @@ import { Component, Input, OnInit, Type } from '@angular/core';
import { PageEvent } from '@angular/material/paginator';
import { PaginationData } from '../../utility/pagination-data';
import { ApiService } from '../../api.service';
import { Model } from '../../model/model';
import { Router } from '@angular/router';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent<T> implements OnInit {
export class ListComponent<T extends Model> implements OnInit {
@Input() apiPath: string;
@Input() itemType: Type<T>;
@Input() columns: {title: string, prop: keyof T}[];
@Input() columns: { title: string, prop: keyof T }[];
paginationData: PaginationData = {};
items: T[] = [];
loading = false;
constructor(private api: ApiService) {
constructor(private api: ApiService, private router: Router) {
}
ngOnInit(): void {
@ -42,4 +44,8 @@ export class ListComponent<T> implements OnInit {
}
}
async editItem(item: T): Promise<void> {
window.localStorage.setItem(this.router.url + '/' + item.id, JSON.stringify(item));
await this.router.navigate([this.router.url, item.id]);
}
}

View file

@ -7,22 +7,30 @@ import { MatTableModule } from '@angular/material/table';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ListComponent } from './list/list.component';
import { EditComponent } from './edit/edit.component';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
@NgModule({
declarations: [TableComponent, ListComponent],
declarations: [TableComponent, ListComponent, EditComponent],
exports: [
TableComponent,
ListComponent
ListComponent,
EditComponent
],
imports: [
CommonModule,
MatProgressBarModule,
MatPaginatorModule,
MatTableModule,
MatButtonModule,
MatIconModule
]
imports: [
CommonModule,
MatProgressBarModule,
MatPaginatorModule,
MatTableModule,
MatButtonModule,
MatIconModule,
MatProgressSpinnerModule,
ReactiveFormsModule,
MatInputModule
]
})
export class SharedComponentsModule { }

View file

@ -15,7 +15,7 @@
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef style="width: 2rem"></th>
<td mat-cell *matCellDef="let item">
<button mat-icon-button color="primary"><mat-icon>edit</mat-icon></button>
<button mat-icon-button color="primary" (click)="editItem.emit(item)"><mat-icon>edit</mat-icon></button>
</td>
<tr mat-header-row *matHeaderRowDef="getPropNames()"></tr>
<tr mat-row *matRowDef="let row; columns: getPropNames()"></tr>

View file

@ -16,6 +16,7 @@ export class TableComponent<T> implements OnInit {
@Input() paginationData: PaginationData = {page: 1, limit: 10};
@Output() pageChange = new EventEmitter<PageEvent>();
@Output() editItem = new EventEmitter<T>();
constructor() {
}

View file

@ -0,0 +1,5 @@
<app-edit [apiPath]="'/users'" [fields]="[
{title: 'E-mail', name: 'email'},
{title: 'Név', name: 'name'},
{title: 'Admin', name: 'isAdmin'}
]"></app-edit>

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserEditComponent } from './user-edit.component';
describe('UserEditComponent', () => {
let component: UserEditComponent;
let fixture: ComponentFixture<UserEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserEditComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.css']
})
export class UserEditComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View file

@ -4,13 +4,15 @@ import { UserListComponent } from './user-list/user-list.component';
import { RouterModule, Routes } from '@angular/router';
import { RouteData } from '../app-routing.module';
import { SharedComponentsModule } from '../shared-components/shared-components.module';
import { UserEditComponent } from './user-edit/user-edit.component';
const routes: Routes = [
{path: '', component: UserListComponent, data: {title: 'Felhasználók'} as RouteData}
{path: '', component: UserListComponent, data: {title: 'Felhasználók'} as RouteData},
{path: ':id', component: UserEditComponent, data: {title: 'Szerkesztés'}}
];
@NgModule({
declarations: [UserListComponent],
declarations: [UserListComponent, UserEditComponent],
imports: [
CommonModule,
RouterModule.forChild(routes),