From fc995858e6e7681a2ca7439bcd40420227dcd4a1 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 21 Jan 2022 17:10:36 +0100 Subject: [PATCH] Add custom material table and list components Automatically querying the specified endpoint for the list data --- frontend/src/app/api.service.ts | 20 ++++++-- .../shared-components/list/list.component.css | 0 .../list/list.component.html | 4 ++ .../list/list.component.spec.ts | 25 ++++++++++ .../shared-components/list/list.component.ts | 48 +++++++++++++++++++ .../shared-components.module.ts | 28 +++++++++++ .../table/table.component.css | 3 ++ .../table/table.component.html | 35 ++++++++++++++ .../table/table.component.spec.ts | 25 ++++++++++ .../table/table.component.ts | 31 ++++++++++++ .../users/user-list/user-list.component.html | 2 +- .../users/user-list/user-list.component.ts | 5 +- frontend/src/app/users/users.module.ts | 4 +- frontend/src/app/utility/pagination-data.ts | 5 ++ 14 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 frontend/src/app/shared-components/list/list.component.css create mode 100644 frontend/src/app/shared-components/list/list.component.html create mode 100644 frontend/src/app/shared-components/list/list.component.spec.ts create mode 100644 frontend/src/app/shared-components/list/list.component.ts create mode 100644 frontend/src/app/shared-components/shared-components.module.ts create mode 100644 frontend/src/app/shared-components/table/table.component.css create mode 100644 frontend/src/app/shared-components/table/table.component.html create mode 100644 frontend/src/app/shared-components/table/table.component.spec.ts create mode 100644 frontend/src/app/shared-components/table/table.component.ts create mode 100644 frontend/src/app/utility/pagination-data.ts diff --git a/frontend/src/app/api.service.ts b/frontend/src/app/api.service.ts index b1c9994..3e58d3a 100644 --- a/frontend/src/app/api.service.ts +++ b/frontend/src/app/api.service.ts @@ -1,7 +1,7 @@ -import {Injectable} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {environment} from '../environments/environment'; -import {LoginService} from './auth/login.service'; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { environment } from '../environments/environment'; +import { LoginService } from './auth/login.service'; @Injectable({ providedIn: 'root' @@ -18,6 +18,18 @@ export class ApiService { }).toPromise(); } + requestPage(url: string, limit: number, page: number): Promise { + const c = url.indexOf('?') === -1 ? '?' : '&'; + return this.request('get', url + c + 'filter=' + encodeURI(JSON.stringify({ + limit, + offset: (page - 1) * limit + })), {}); + } + + requestItemCount(url: string): Promise { + return this.request('get', url + '/count', {}); + } + async logout(): Promise { await this.request('post', '/users/logout', ''); this.loginService.token = null; diff --git a/frontend/src/app/shared-components/list/list.component.css b/frontend/src/app/shared-components/list/list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/shared-components/list/list.component.html b/frontend/src/app/shared-components/list/list.component.html new file mode 100644 index 0000000..cf1ed1d --- /dev/null +++ b/frontend/src/app/shared-components/list/list.component.html @@ -0,0 +1,4 @@ + + + diff --git a/frontend/src/app/shared-components/list/list.component.spec.ts b/frontend/src/app/shared-components/list/list.component.spec.ts new file mode 100644 index 0000000..a5d3a5c --- /dev/null +++ b/frontend/src/app/shared-components/list/list.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListComponent } from './list.component'; + +describe('ListComponent', () => { + let component: ListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ListComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/shared-components/list/list.component.ts b/frontend/src/app/shared-components/list/list.component.ts new file mode 100644 index 0000000..c8c39b3 --- /dev/null +++ b/frontend/src/app/shared-components/list/list.component.ts @@ -0,0 +1,48 @@ +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'; + +@Component({ + selector: 'app-list', + templateUrl: './list.component.html', + styleUrls: ['./list.component.css'] +}) +export class ListComponent implements OnInit { + + @Input() apiPath: string; + @Input() itemType: Type; + + paginationData: PaginationData = {}; + items: T[] = []; + columns: { title: string, prop: string }[] = [ + {title: 'Név', prop: 'name'}, + {title: 'Admin', prop: 'isAdmin'} + ]; + loading = false; + + constructor(private api: ApiService) { + } + + ngOnInit(): void { + this.getItems(10, 1).catch(e => console.error(e)); + } + + async handlePageChange(event: PageEvent): Promise { + await this.getItems(event.pageSize, event.pageIndex + 1); + } + + async getItems(limit: number, page: number): Promise { + try { + this.loading = true; + const total = await this.api.requestItemCount(this.apiPath); + this.paginationData.page = page; + this.paginationData.limit = limit; + this.paginationData.total = total; + this.items = await this.api.requestPage(this.apiPath, limit, page); + } finally { + this.loading = false; + } + } + +} diff --git a/frontend/src/app/shared-components/shared-components.module.ts b/frontend/src/app/shared-components/shared-components.module.ts new file mode 100644 index 0000000..53d355b --- /dev/null +++ b/frontend/src/app/shared-components/shared-components.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TableComponent } from './table/table.component'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTableModule } from '@angular/material/table'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { ListComponent } from './list/list.component'; + + + +@NgModule({ + declarations: [TableComponent, ListComponent], + exports: [ + TableComponent, + ListComponent + ], + imports: [ + CommonModule, + MatProgressBarModule, + MatPaginatorModule, + MatTableModule, + MatButtonModule, + MatIconModule + ] +}) +export class SharedComponentsModule { } diff --git a/frontend/src/app/shared-components/table/table.component.css b/frontend/src/app/shared-components/table/table.component.css new file mode 100644 index 0000000..1922e7f --- /dev/null +++ b/frontend/src/app/shared-components/table/table.component.css @@ -0,0 +1,3 @@ +table { + width: 100%; +} diff --git a/frontend/src/app/shared-components/table/table.component.html b/frontend/src/app/shared-components/table/table.component.html new file mode 100644 index 0000000..2368d2d --- /dev/null +++ b/frontend/src/app/shared-components/table/table.component.html @@ -0,0 +1,35 @@ +
+
+
+ +
+
+
+ + + + + + + + + + + + + +
{{col.title}}{{ item[col.prop]}} + +
+
+ +
+ +
+
diff --git a/frontend/src/app/shared-components/table/table.component.spec.ts b/frontend/src/app/shared-components/table/table.component.spec.ts new file mode 100644 index 0000000..7458024 --- /dev/null +++ b/frontend/src/app/shared-components/table/table.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TableComponent } from './table.component'; + +describe('AppTableComponent', () => { + let component: TableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ TableComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/shared-components/table/table.component.ts b/frontend/src/app/shared-components/table/table.component.ts new file mode 100644 index 0000000..95dbc86 --- /dev/null +++ b/frontend/src/app/shared-components/table/table.component.ts @@ -0,0 +1,31 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { PaginationData } from '../../utility/pagination-data'; +import { PageEvent } from '@angular/material/paginator'; + +@Component({ + selector: 'app-table', + templateUrl: './table.component.html', + styleUrls: ['./table.component.css'] +}) +export class TableComponent implements OnInit { + + @Input() showHeader = false; + @Input() items: T[] = []; + @Input() loading = false; + @Input() columns: { title: string, prop: string }[] = []; + @Input() paginationData: PaginationData = {page: 1, limit: 10}; + + @Output() pageChange = new EventEmitter(); + + constructor() { + } + + ngOnInit(): void { + } + + getPropNames(): string[] { + return this.columns.map(col => col.prop).concat('actions'); + } +} + + diff --git a/frontend/src/app/users/user-list/user-list.component.html b/frontend/src/app/users/user-list/user-list.component.html index 16029d7..71642e9 100644 --- a/frontend/src/app/users/user-list/user-list.component.html +++ b/frontend/src/app/users/user-list/user-list.component.html @@ -1 +1 @@ -

user-list works!

+ diff --git a/frontend/src/app/users/user-list/user-list.component.ts b/frontend/src/app/users/user-list/user-list.component.ts index 0098a48..4d51771 100644 --- a/frontend/src/app/users/user-list/user-list.component.ts +++ b/frontend/src/app/users/user-list/user-list.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { User } from '../../model/user.model'; @Component({ selector: 'app-user-list', @@ -6,8 +7,10 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./user-list.component.css'] }) export class UserListComponent implements OnInit { + itemType = User; - constructor() { } + constructor() { + } ngOnInit(): void { } diff --git a/frontend/src/app/users/users.module.ts b/frontend/src/app/users/users.module.ts index f453eff..d33c004 100644 --- a/frontend/src/app/users/users.module.ts +++ b/frontend/src/app/users/users.module.ts @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common'; 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'; const routes: Routes = [ {path: '', component: UserListComponent, data: {title: 'Felhasználók'} as RouteData} @@ -12,7 +13,8 @@ const routes: Routes = [ declarations: [UserListComponent], imports: [ CommonModule, - RouterModule.forChild(routes) + RouterModule.forChild(routes), + SharedComponentsModule ] }) export class UsersModule { } diff --git a/frontend/src/app/utility/pagination-data.ts b/frontend/src/app/utility/pagination-data.ts new file mode 100644 index 0000000..50dd1bf --- /dev/null +++ b/frontend/src/app/utility/pagination-data.ts @@ -0,0 +1,5 @@ +export class PaginationData { + total?: number; + limit?: number; + page?: number; +}