1 | import { Repository, SelectQueryBuilder } from "typeorm";
|
2 | import { Request } from "express";
|
3 |
|
4 | export class Element {
|
5 | _class: string;
|
6 | text: string;
|
7 | }
|
8 |
|
9 | export class TableConfiguration {
|
10 | _class: string = "";
|
11 | ordersBy: Number[] = [];
|
12 | header: Element[] = [];
|
13 | data: Element[][] = [];
|
14 |
|
15 | addClass(_class: string): TableConfiguration {
|
16 | this._class += _class + " ";
|
17 | return this;
|
18 | }
|
19 |
|
20 | addHeader(text: string, _class?: string): TableConfiguration {
|
21 | this.header.push({ text: text, _class: _class ? _class : "" });
|
22 | this.ordersBy.push(0);
|
23 | return this;
|
24 | }
|
25 |
|
26 | addData(texts: string[], _classes?: string[]): TableConfiguration {
|
27 | let c: Element[] = [];
|
28 | for (let i = 0; i < texts.length; i++) {
|
29 | c.push({
|
30 | text: texts[i],
|
31 | _class: _classes && _classes[i] ? _classes[i] : ""
|
32 | });
|
33 | }
|
34 | this.data.push(c);
|
35 | return this;
|
36 | }
|
37 | }
|
38 |
|
39 | export class PaginationConfiguration {
|
40 | total = 0;
|
41 | pageSize = 10;
|
42 | pageCount = 0;
|
43 | currentPage = 0;
|
44 | left = 0;
|
45 | right = 0;
|
46 | maxPages = 2;
|
47 | _class: string = "";
|
48 |
|
49 | calculate() {
|
50 | this.pageCount = Math.ceil(this.total / this.pageSize);
|
51 | this.left = Math.max(0, this.currentPage - this.maxPages);
|
52 | this.right = Math.min(
|
53 | this.pageCount,
|
54 | this.currentPage + 1 + this.maxPages
|
55 | );
|
56 | }
|
57 |
|
58 | addClass(_class: string): PaginationConfiguration {
|
59 | this._class += _class + " ";
|
60 | return this;
|
61 | }
|
62 | }
|
63 |
|
64 | export class DatatableConfiguration {
|
65 | private table: TableConfiguration = new TableConfiguration();
|
66 | private repository: Repository<any>;
|
67 | private req: Request;
|
68 | protected urlNoPage: string;
|
69 | protected urlNoOrder: string;
|
70 | protected urlNoPageNoOrder: string;
|
71 | protected urlNoFilter: string;
|
72 | private map: string[];
|
73 | private _classes: string[] = [];
|
74 | private mapTransformers: any = {};
|
75 | private pagination: PaginationConfiguration = new PaginationConfiguration();
|
76 | private alias: string = "e";
|
77 |
|
78 | constructor(repository: Repository<any>, req: Request, alias?: string) {
|
79 | this.repository = repository;
|
80 | this.req = req;
|
81 | this.setupPageRequested();
|
82 | this.urlNoPage = getUrlWithoutPage(this.req);
|
83 | this.urlNoOrder = getUrlWithoutOrder(this.req);
|
84 | this.urlNoPageNoOrder = getUrlWithoutPageOrOrder(this.req);
|
85 | this.urlNoFilter = getUrlWithoutFilter(this.req);
|
86 | if (alias) {
|
87 | this.alias = alias;
|
88 | }
|
89 | }
|
90 |
|
91 | public setPageSize(size: number) {
|
92 | this.pagination.pageSize = size;
|
93 | }
|
94 |
|
95 | public addTableClass(_class: string): DatatableConfiguration {
|
96 | this.table.addClass(_class);
|
97 | return this;
|
98 | }
|
99 |
|
100 | public addTableHeader(
|
101 | text: string,
|
102 | _class?: string
|
103 | ): DatatableConfiguration {
|
104 | this.table.addHeader(text, _class);
|
105 | return this;
|
106 | }
|
107 |
|
108 | public addDataMap(
|
109 | map: string[],
|
110 | _classes?: string[]
|
111 | ): DatatableConfiguration {
|
112 | this.map = map;
|
113 | if (_classes) {
|
114 | this._classes = _classes;
|
115 | } else {
|
116 | this._classes = [];
|
117 | for (let i = 0; i < this.map.length; i++) {
|
118 | this._classes.push("");
|
119 | }
|
120 | }
|
121 | return this;
|
122 | }
|
123 |
|
124 | public addDataTransformer(
|
125 | label: string,
|
126 | exe: (value: any, entity: any) => any
|
127 | ): DatatableConfiguration {
|
128 | this.mapTransformers[label] = exe;
|
129 | return this;
|
130 | }
|
131 |
|
132 | public addPaginationClass(_class: string): DatatableConfiguration {
|
133 | this.pagination.addClass(_class);
|
134 | return this;
|
135 | }
|
136 |
|
137 | private setupPageRequested() {
|
138 | this.pagination.currentPage = 0;
|
139 | if (this.req.query.page) {
|
140 | this.pagination.currentPage = Number(this.req.query.page);
|
141 | if (this.pagination.currentPage > 0) {
|
142 | this.pagination.currentPage -= 1;
|
143 | }
|
144 | }
|
145 | }
|
146 |
|
147 | private getQueryValue(key: string): any[] {
|
148 | for (let q in this.req.query) {
|
149 | if (q.toLowerCase() !== key) {
|
150 | continue;
|
151 | }
|
152 | if (this.req.query[q] instanceof Array) {
|
153 | return this.req.query[q];
|
154 | }
|
155 | return [this.req.query[q]];
|
156 | }
|
157 | return [];
|
158 | }
|
159 |
|
160 | private addOrderBy(
|
161 | query: SelectQueryBuilder<any>
|
162 | ): SelectQueryBuilder<any> {
|
163 | let ordersBy = this.getQueryValue("orderby");
|
164 | if (ordersBy.length > 0) {
|
165 | let order = ordersBy[0].split(":");
|
166 | this.setOrderBy(order[0], order[1]);
|
167 | query = query.orderBy(
|
168 | this.alias + "." + order[0],
|
169 | order[1] == "desc" ? "DESC" : "ASC"
|
170 | );
|
171 | for (let i = 1; i < ordersBy.length; i++) {
|
172 | let order = ordersBy[i].split(":");
|
173 | this.setOrderBy(order[0], order[1]);
|
174 | query = query.addOrderBy(
|
175 | this.alias + "." + order[0],
|
176 | order[1] == "desc" ? "DESC" : "ASC"
|
177 | );
|
178 | }
|
179 | }
|
180 | return query;
|
181 | }
|
182 |
|
183 | private setOrderBy(column: string, order: string) {
|
184 | for (let i = 0; i < this.map.length; i++) {
|
185 | if (this.map[i] == column) {
|
186 | this.table.ordersBy[i] = order == "desc" ? -1 : 1;
|
187 | }
|
188 | }
|
189 | }
|
190 |
|
191 | private addFilterBy(
|
192 | query: SelectQueryBuilder<any>
|
193 | ): SelectQueryBuilder<any> {
|
194 | let filtersBy = this.getQueryValue("filterby");
|
195 | if (filtersBy.length) {
|
196 | let f = filtersBy[0].split(":");
|
197 | let o: any = {};
|
198 | o[f[0] + "_" + 0] = f[1];
|
199 | query = query.where(this.alias + "." + f[0] + " = :" + f[0] + "_" + 0, o);
|
200 | for (let i = 1; i < filtersBy.length; i++) {
|
201 | let f = filtersBy[i].split(":");
|
202 | let o: any = {};
|
203 | o[f[0] + "_" + i] = f[1];
|
204 | query = query.andWhere(
|
205 | this.alias + "." + f[0] + " = :" + f[0] + "_" + i,
|
206 | o
|
207 | );
|
208 | }
|
209 | }
|
210 | return query;
|
211 | }
|
212 |
|
213 | public async fetchData(query?: SelectQueryBuilder<any>): Promise<void> {
|
214 | if (!query) {
|
215 | let select: string[] = [];
|
216 | this.map.forEach(v => {
|
217 | if (!v.startsWith(":")) {
|
218 | select.push(this.alias + "." + v.replace("-", ""));
|
219 | }
|
220 | });
|
221 | query = this.repository.createQueryBuilder(this.alias).select(select);
|
222 | }
|
223 |
|
224 | query = this.addFilterBy(query);
|
225 |
|
226 | this.pagination.total = await query.getCount();
|
227 | this.pagination.calculate();
|
228 |
|
229 | query = this.addOrderBy(query);
|
230 |
|
231 | let skip = this.pagination.currentPage * this.pagination.pageSize;
|
232 | query = query.skip(skip).take(this.pagination.pageSize);
|
233 |
|
234 | let result = await query.getMany();
|
235 |
|
236 | for (let r of result) {
|
237 | let line: string[] = [];
|
238 | for (let label of this.map) {
|
239 | if (label.startsWith("-")) continue;
|
240 | let cell = r[label];
|
241 | if (this.mapTransformers[label]) {
|
242 | cell = this.mapTransformers[label](cell, r);
|
243 | }
|
244 | line.push(cell);
|
245 | }
|
246 | this.table.addData(line, this._classes);
|
247 | }
|
248 | }
|
249 | }
|
250 |
|
251 | function getUrlWithoutPage(req: Request): string {
|
252 | return getUrlWithoutParameter(req, ["page"]);
|
253 | }
|
254 |
|
255 | function getUrlWithoutOrder(req: Request): string {
|
256 | return getUrlWithoutParameter(req, ["orderby"]);
|
257 | }
|
258 |
|
259 | function getUrlWithoutPageOrOrder(req: Request): string {
|
260 | return getUrlWithoutParameter(req, ["orderby", "page"]);
|
261 | }
|
262 |
|
263 | function getUrlWithoutFilter(req: Request): string {
|
264 | return getUrlWithoutParameter(req, ["filter", "filterby"]);
|
265 | }
|
266 |
|
267 | function getUrlWithoutParameter(req: Request, parameters: string[]): string {
|
268 | let u = (req.baseUrl + req.path).replace(/\/$/, "") + "?";
|
269 | for (let key in req.query) {
|
270 | var found = false;
|
271 | for (let p of parameters) {
|
272 | if (key.toLowerCase() == p) {
|
273 | found = true;
|
274 | break;
|
275 | }
|
276 | }
|
277 | if (found) {
|
278 | continue;
|
279 | }
|
280 | u += generateQueryValue(key, req.query[key]);
|
281 | }
|
282 | return u;
|
283 | }
|
284 |
|
285 | function generateQueryValue(key: string, q: any): string {
|
286 | let m = key + "=";
|
287 | if (q instanceof Array) {
|
288 | m += q[0] + "&";
|
289 | for (let i = 1; i < q.length; i++) {
|
290 | m += key + "=" + q[i] + "&";
|
291 | }
|
292 | return m;
|
293 | }
|
294 | return m + q + "&";
|
295 | }
|