Skip to content

Commit 5a64086

Browse files
committed
add query and manipulation abstract class to implement a list and modal without ng-bootstrap
1 parent 4bcc011 commit 5a64086

File tree

5 files changed

+311
-213
lines changed

5 files changed

+311
-213
lines changed

projects/fusio-sdk/src/lib/abstract/detail.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
22

3+
/**
4+
* This component is only a basic view component which renders a provided entity. It should only render the provided
5+
* JSON data it should not contain any additional logic to request data from an endpoint
6+
*/
37
@Component({
48
template: '',
59
})

projects/fusio-sdk/src/lib/abstract/list.ts

Lines changed: 15 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,27 @@
1-
import {Component, OnInit} from '@angular/core';
2-
import {CollectionCategoryQuery} from "fusio-sdk/dist/src/generated/backend/CollectionCategoryQuery";
1+
import {Component} from '@angular/core';
32
import {ActivatedRoute, Router} from "@angular/router";
43
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
5-
import {AxiosResponse} from "axios";
6-
import {Collection} from "fusio-sdk/dist/src/generated/backend/Collection";
7-
import {Message} from "fusio-sdk/dist/src/generated/backend/Message";
8-
import {CollectionQuery} from "fusio-sdk/dist/src/generated/backend/CollectionQuery";
9-
import {FusioService} from "../service/fusio.service";
104
import {ClientAbstract} from "sdkgen-client";
5+
import {FusioService} from "../service/fusio.service";
116
import {ErrorService} from "../service/error.service";
127
import {EventService} from "../service/event.service";
138
import {Result} from "./modal";
9+
import {ModelId, Query} from "./query";
1410

11+
/**
12+
* List panel which uses a modal to CRUD entities. Note it depends on ng-bootstrap so you can use it only if your
13+
* project also uses ng-bootstrap
14+
*/
1515
@Component({
1616
template: '',
1717
})
18-
export abstract class List<C extends ClientAbstract, T extends ModelId> implements OnInit {
19-
20-
public search: string = '';
21-
public totalResults: number = 0;
22-
public entries: Array<T> = [];
23-
public selected?: T;
24-
public page: number = 1;
25-
public pageSize: number = 16;
26-
public response?: Message;
27-
public loading: boolean = true;
28-
29-
constructor(protected fusio: FusioService<C>, protected route: ActivatedRoute, protected router: Router, protected modalService: NgbModal, protected event: EventService, protected error: ErrorService) {
30-
}
31-
32-
async ngOnInit(): Promise<void> {
33-
this.startLoading();
34-
this.route.queryParams.subscribe(async params => {
35-
let page, search;
36-
if (params['page']) {
37-
page = parseInt(params['page']);
38-
}
39-
if (params['search']) {
40-
search = params['search'];
41-
}
42-
if (!this.hasQueryParamsChange(page, search)) {
43-
return;
44-
}
45-
this.startLoading();
46-
this.page = page || 1;
47-
this.search = search || '';
48-
await this.doList();
49-
});
50-
51-
this.route.paramMap.subscribe(async params => {
52-
const id = params.get('id');
53-
if (id) {
54-
this.startLoading();
55-
await this.doGet(id);
56-
}
57-
});
58-
}
59-
60-
async doList() {
61-
try {
62-
const response = await this.getAll(this.getCollectionQuery());
63-
64-
this.totalResults = response.data.totalResults || 0;
65-
this.entries = response.data.entry || [];
66-
67-
this.onList();
68-
} catch (error) {
69-
this.response = this.error.convert(error);
70-
}
71-
72-
// in case we are not at a specific route redirect to the first
73-
const isDetailRoute: boolean|undefined = this.route.routeConfig?.path?.endsWith(':id');
74-
if (this.entries.length > 0 && this.entries[0].id && isDetailRoute === false && !this.selected) {
75-
await this.doGet('' + this.entries[0].id);
76-
}
77-
78-
this.finishLoading();
79-
}
80-
81-
protected getCollectionQuery(): CollectionQuery {
82-
let query: CollectionQuery = {};
83-
query.startIndex = (this.page - 1) * this.pageSize;
84-
query.count = this.pageSize;
85-
if (this.search) {
86-
query.search = this.search;
87-
}
88-
return query;
89-
}
90-
91-
async doGet(id: string) {
92-
try {
93-
const response = await this.get(id);
94-
95-
this.selected = response.data;
96-
97-
this.onGet();
98-
} catch (error) {
99-
this.response = this.error.convert(error);
100-
}
18+
export abstract class List<C extends ClientAbstract, T extends ModelId> extends Query<C, T> {
10119

102-
this.finishLoading();
103-
}
104-
105-
async doSearch(page?: number, search?: string) {
106-
if (!this.hasQueryParamsChange(page, search) || this.loading) {
107-
return;
108-
}
109-
if (this.selected) {
110-
await this.router.navigate([this.getRoute(), this.selected.id], {
111-
queryParams: this.getQueryParams(page, search)
112-
});
113-
} else {
114-
await this.router.navigate([this.getRoute()], {
115-
queryParams: this.getQueryParams(page, search)
116-
});
117-
}
118-
return false;
119-
}
20+
protected modalService: NgbModal;
12021

121-
async doSelect(entry: T) {
122-
await this.router.navigate([this.getRoute(), entry.id], {
123-
queryParams: this.getQueryParams(this.page, this.search)
124-
});
22+
constructor(fusio: FusioService<C>, route: ActivatedRoute, router: Router, event: EventService, error: ErrorService, modalService: NgbModal) {
23+
super(fusio, route, router, event, error);
24+
this.modalService = modalService;
12525
}
12626

12727
openCreateDialog() {
@@ -136,7 +36,7 @@ export abstract class List<C extends ClientAbstract, T extends ModelId> implemen
13636

13737
await this.doList();
13838
}
139-
})
39+
});
14040
}
14141

14242
openUpdateDialog(entity: T) {
@@ -152,8 +52,7 @@ export abstract class List<C extends ClientAbstract, T extends ModelId> implemen
15252

15353
await this.doList();
15454
}
155-
})
156-
55+
});
15756
}
15857

15958
openDeleteDialog(entity: T) {
@@ -169,60 +68,11 @@ export abstract class List<C extends ClientAbstract, T extends ModelId> implemen
16968

17069
await this.doList();
17170
}
172-
})
173-
}
174-
175-
getQueryParams(page?: number, search?: string): QueryParams {
176-
const queryParams: QueryParams = {};
177-
if (page) {
178-
queryParams.page = page;
179-
}
180-
if (search) {
181-
queryParams.search = search;
182-
}
183-
return queryParams;
184-
}
185-
186-
hasQueryParamsChange(page?: number, search?: string): boolean {
187-
return this.page !== page || this.search !== search;
188-
}
189-
190-
private startLoading(): void {
191-
this.loading = true;
192-
}
193-
194-
private finishLoading(): void {
195-
setTimeout(() => {
196-
this.loading = false;
197-
}, 100);
198-
}
199-
200-
protected abstract getAll(query: CollectionCategoryQuery): Promise<AxiosResponse<Collection<T>>>;
201-
protected abstract get(id: string): Promise<AxiosResponse<T>>;
202-
protected abstract getDetailComponent(): any;
203-
protected abstract getRoute(): string;
204-
205-
protected onList(): void
206-
{
207-
this.event.dispatchModelList(this.getRoute());
208-
}
209-
210-
protected onGet(): void
211-
{
212-
this.event.dispatchModelDetail(this.selected, this.getRoute());
71+
});
21372
}
21473

21574
}
21675

217-
export interface QueryParams {
218-
page?: number
219-
search?: string
220-
}
221-
222-
export interface ModelId {
223-
id?: number|string
224-
}
225-
22676
export enum Mode {
22777
Create = 1,
22878
Update,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import {Component, Input, OnInit} from "@angular/core";
2+
import {Message} from "fusio-sdk/dist/src/generated/backend/Message";
3+
import {Mode} from "./list";
4+
import {AxiosResponse} from "axios";
5+
import {ClientAbstract} from "sdkgen-client";
6+
import {FusioService} from "../service/fusio.service";
7+
import {ErrorService} from "../service/error.service";
8+
import {ModelId} from "./query";
9+
10+
/**
11+
* Base component if you want to create a panel to create, update or delete an entity. If you extend this class you
12+
* need to implement the {@see create}, {@see update} and {@see delete} method. This class only uses native angular
13+
* components so you can use it independently of your UI framework
14+
*/
15+
@Component({
16+
template: '',
17+
})
18+
export abstract class Manipulation<C extends ClientAbstract, T extends ModelId> implements OnInit {
19+
20+
response?: Message;
21+
loading: boolean = false;
22+
23+
@Input() mode: Mode = Mode.Create;
24+
@Input() entity: T = this.newEntity();
25+
26+
protected fusio: FusioService<C>;
27+
protected error: ErrorService;
28+
29+
constructor(fusio: FusioService<C>, error: ErrorService) {
30+
this.fusio = fusio;
31+
this.error = error;
32+
}
33+
34+
ngOnInit(): void {
35+
}
36+
37+
async submit() {
38+
if (!this.entity) {
39+
return;
40+
}
41+
42+
this.loading = true;
43+
44+
const data = this.entity;
45+
46+
try {
47+
let response;
48+
if (this.mode === Mode.Create) {
49+
response = await this.create(data);
50+
} else if (this.mode === Mode.Update) {
51+
response = await this.update(data);
52+
} else if (this.mode === Mode.Delete) {
53+
response = await this.delete(data);
54+
}
55+
56+
this.loading = false;
57+
58+
if (response) {
59+
this.onResponse(response, data);
60+
}
61+
} catch (error) {
62+
this.loading = false;
63+
this.response = this.error.convert(error);
64+
}
65+
}
66+
67+
protected onResponse(response: AxiosResponse<Message>, entity: T): void {
68+
}
69+
70+
protected abstract create(entity: T): Promise<AxiosResponse<Message>|void>;
71+
protected abstract update(entity: T): Promise<AxiosResponse<Message>|void>;
72+
protected abstract delete(entity: T): Promise<AxiosResponse<Message>|void>;
73+
protected abstract newEntity(): T;
74+
75+
}

projects/fusio-sdk/src/lib/abstract/modal.ts

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,38 @@
1-
import {Component, Input, OnInit} from "@angular/core";
1+
import {Component} from "@angular/core";
22
import {Message} from "fusio-sdk/dist/src/generated/backend/Message";
3-
import {Mode, ModelId} from "./list";
43
import {NgbActiveModal, NgbModal} from "@ng-bootstrap/ng-bootstrap";
54
import {AxiosResponse} from "axios";
6-
import {FusioService} from "../service/fusio.service";
75
import {ClientAbstract} from "sdkgen-client";
6+
import {FusioService} from "../service/fusio.service";
87
import {ErrorService} from "../service/error.service";
8+
import {Manipulation} from "./manipulation";
9+
import {ModelId} from "./query";
910

11+
/**
12+
* Manipulation panel which uses a modal to create, update or delete an entity. Note it depends on ng-bootstrap so you
13+
* can use it only if your project also uses ng-bootstrap
14+
*/
1015
@Component({
1116
template: '',
1217
})
13-
export abstract class Modal<C extends ClientAbstract, T extends ModelId> implements OnInit {
14-
15-
response?: Message;
16-
loading: boolean = false;
18+
export abstract class Modal<C extends ClientAbstract, T extends ModelId> extends Manipulation<C, T> {
1719

18-
@Input() mode: Mode = Mode.Create;
19-
@Input() entity: T = this.newEntity();
20+
protected modalService: NgbModal;
21+
public modal: NgbActiveModal;
2022

21-
constructor(protected fusio: FusioService<C>, protected error: ErrorService, protected modalService: NgbModal, public modal: NgbActiveModal) { }
22-
23-
ngOnInit(): void {
23+
constructor(fusio: FusioService<C>, error: ErrorService, modalService: NgbModal, modal: NgbActiveModal) {
24+
super(fusio, error);
25+
this.modalService = modalService;
26+
this.modal = modal;
2427
}
2528

26-
async submit() {
27-
if (!this.entity) {
28-
return;
29-
}
30-
31-
this.loading = true;
32-
33-
const data = this.entity;
34-
35-
try {
36-
let response;
37-
if (this.mode === Mode.Create) {
38-
response = await this.create(data);
39-
} else if (this.mode === Mode.Update) {
40-
response = await this.update(data);
41-
} else if (this.mode === Mode.Delete) {
42-
response = await this.delete(data);
43-
}
44-
45-
this.loading = false;
46-
47-
if (response) {
48-
this.modal.close({
49-
entity: data,
50-
response: response.data,
51-
});
52-
}
53-
} catch (error) {
54-
this.loading = false;
55-
this.response = this.error.convert(error);
56-
}
29+
protected onResponse(response: AxiosResponse<Message>, entity: T) {
30+
this.modal.close({
31+
entity: entity,
32+
response: response.data,
33+
});
5734
}
5835

59-
protected abstract create(entity: T): Promise<AxiosResponse<Message>|void>;
60-
protected abstract update(entity: T): Promise<AxiosResponse<Message>|void>;
61-
protected abstract delete(entity: T): Promise<AxiosResponse<Message>|void>;
62-
protected abstract newEntity(): T;
63-
6436
}
6537

6638
export interface Result<T> {

0 commit comments

Comments
 (0)