Skip to content

Commit

Permalink
fix: changes the query sort mechanism to improve compatibility with v…
Browse files Browse the repository at this point in the history
…uetify`s data-table component by supporting both single and multiple sort properties
  • Loading branch information
mad-nuts committed Feb 25, 2022
1 parent a8bc079 commit e787248
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 105 deletions.
21 changes: 17 additions & 4 deletions iris-client-fe/src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ import globalAxios, {
CancelTokenSource,
Method,
} from "axios";
import axios from "axios";
import _castArray from "lodash/castArray";
import { join } from "@/utils/misc";
/**
*
* @export
*/
export type DataQuery = {
size: number;
page: number;
sort?: string | null;
sort?: string | string[] | null;
status?: DataRequestStatus | null;
search?: string | null;
folder?: string;
Expand Down Expand Up @@ -113,6 +114,18 @@ export const getSortAttribute = function (key: string): string {
return sortAttributes[key];
};

const mapSortAttribute = (sort: string): string => {
const sortArgs = sort.split(",");
return join([getSortAttribute(sortArgs[0]) || sortArgs[0], sortArgs[1]], ",");
};

export const mapSortAttributes = (
sort: DataQuery["sort"]
): DataQuery["sort"] => {
if (!sort) return sort;
return _castArray(sort).map(mapSortAttribute);
};

export const apiRequestBuilder =
(axiosInstance: AxiosInstance = globalAxios): ApiRequestFunction =>
<T = any, D = any>(
Expand All @@ -132,11 +145,11 @@ export const apiRequestBuilder =
};

export const cancelTokenProvider = () => {
let source: CancelTokenSource = axios.CancelToken.source();
let source: CancelTokenSource = globalAxios.CancelToken.source();
return (): CancelToken => {
try {
source.cancel("request canceled");
source = axios.CancelToken.source();
source = globalAxios.CancelToken.source();
} catch (e) {
// ignored
}
Expand Down
14 changes: 12 additions & 2 deletions iris-client-fe/src/components/iris-data-table.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<template>
<v-data-table
v-bind="$attrs"
v-on="$listeners"
v-on="listeners"
:loading="loading"
:items="filteredItems"
:server-items-length="serverItemsLength"
v-model="model"
:class="[
$attrs.class,
Expand Down Expand Up @@ -31,6 +32,7 @@
import { Component, Vue } from "vue-property-decorator";
import DataTableSelectAll from "@/components/data-table-select-all.vue";
import { PropType } from "vue";
import _omit from "lodash/omit";
type FilterFunction = <T>(value: T, index: number, array: T[]) => boolean;
Expand All @@ -57,6 +59,10 @@ const IrisDataTableProps = Vue.extend({
type: Function as PropType<FilterFunction | null>,
default: null,
},
serverItemsLength: {
type: Number,
default: -1,
},
},
});
@Component({
Expand All @@ -65,9 +71,13 @@ const IrisDataTableProps = Vue.extend({
},
})
export default class IrisDataTable extends IrisDataTableProps {
get listeners(): Record<string, unknown> {
return _omit(this.$listeners, ["input"]);
}
currentItems = [];
get filteredItems() {
if (this.filter) {
// use filter only for local data -> this.serverItemsLength <= -1
if (this.filter && this.serverItemsLength <= -1) {
return this.items.filter(this.filter);
}
return this.items;
Expand Down
66 changes: 25 additions & 41 deletions iris-client-fe/src/components/pageable/data-query-handler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@

<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
import { DataQuery, getSortAttribute } from "@/api/common";
import {
DEFAULT_PAGE_SIZE,
getPageFromRouteWithDefault,
getPageSizeFromRouteWithDefault,
getStatusFilterFromRoute,
getStringParamFromRouteWithOptionalFallback,
} from "@/utils/pagination";
import { DataQuery, mapSortAttributes } from "@/api/common";
import { DEFAULT_PAGE_SIZE, getParamFromRoute } from "@/utils/pagination";
import _mapValues from "lodash/mapValues";
import { join } from "@/utils/misc";
import { Location } from "vue-router";
const DataQueryHandlerProps = Vue.extend({
inheritAttrs: false,
Expand Down Expand Up @@ -43,18 +37,12 @@ export default class DataQueryHandler extends DataQueryHandlerProps {
mounted() {
if (this.routeControl) {
this.query = {
size: getPageSizeFromRouteWithDefault(this.$route),
page: Math.max(0, getPageFromRouteWithDefault(this.$route) - 1),
status: getStatusFilterFromRoute(this.$route),
sort: getStringParamFromRouteWithOptionalFallback("sort", this.$route),
search: getStringParamFromRouteWithOptionalFallback(
"search",
this.$route
),
folder: getStringParamFromRouteWithOptionalFallback(
"folder",
this.$route
),
size: getParamFromRoute("size", this.$route) || DEFAULT_PAGE_SIZE,
page: Math.max(0, (getParamFromRoute("page", this.$route) || 1) - 1),
status: getParamFromRoute("status", this.$route),
sort: getParamFromRoute("sort", this.$route),
search: getParamFromRoute("search", this.$route),
folder: getParamFromRoute("folder", this.$route),
};
}
this.initialized = true;
Expand All @@ -63,36 +51,32 @@ export default class DataQueryHandler extends DataQueryHandlerProps {
@Watch("query", { immediate: true, deep: true })
onQueryChange(newValue: DataQuery) {
if (this.routeControl && !this.initialized) return;
let sort = newValue.sort;
if (sort) {
const sortModel = sort.split(",");
sort = join(
[getSortAttribute(sortModel[0]) || sortModel[0], sortModel[1]],
","
);
}
const query = {
...newValue,
sort,
sort: mapSortAttributes(newValue.sort),
};
if (this.routeControl) {
this.updateRoute(query);
this.updateLocation(query);
}
this.$emit("query:update", query);
}
updateRoute(query: DataQuery): void {
const routeQuery: Record<string, unknown> = {
...this.$route.query,
...query,
page: `${(query?.page || 0) + 1}`,
};
updateLocation(query: DataQuery): void {
const locationQuery: Location["query"] = _mapValues(
{
...this.$route.query,
...query,
page: `${(query?.page || 0) + 1}`,
},
(val) => {
if (Array.isArray(val)) return val;
return val ? `${val}` : undefined;
}
);
this.$router
.replace({
name: this.$route.name as string | undefined,
query: _mapValues(routeQuery, (val) => {
return val ? `${val}` : undefined;
}),
name: this.$route.name || undefined,
query: locationQuery,
})
.catch(() => {
// ignored
Expand Down
77 changes: 44 additions & 33 deletions iris-client-fe/src/components/sortable-data-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { TableSort, TableSortDirection } from "@/server/utils/pagination";
import _omit from "lodash/omit";
import { PropType } from "vue";
import { DEFAULT_ITEMS_PER_PAGE_OPTIONS } from "@/utils/pagination";
import _map from "lodash/map";
import _castArray from "lodash/castArray";
export const getSortDir = (dir: unknown): TableSortDirection | undefined => {
switch (dir) {
Expand All @@ -36,7 +38,9 @@ const SortableDataTableProps = Vue.extend({
inheritAttrs: false,
props: {
sort: {
type: [Object, String],
type: [Array, String] as PropType<
string | TableSort | (string | TableSort)[] | undefined
>,
default: undefined,
},
querySort: {
Expand Down Expand Up @@ -76,51 +80,58 @@ export default class SortableDataTable extends SortableDataTableProps {
};
}
get sortBy(): string[] {
return this.sortModel?.col ? [this.sortModel.col] : [];
get sortBy(): string | string[] | undefined {
const s = _map(this.sortModel, "col");
return s.length <= 1 ? s[0] : s;
}
set sortBy(value: string[]) {
this.sortModel =
value.length > 0
? {
col: value[0],
dir: this.sortModel?.dir || TableSortDirection.ASC,
}
: undefined;
set sortBy(value: string | string[] | undefined) {
if (!value || value.length <= 0) {
this.sortModel = [];
} else {
this.sortModel = _castArray(value).map((col, index) => {
return {
col: col,
dir: this.sortModel[index]?.dir || TableSortDirection.ASC,
};
});
}
}
get sortDesc(): boolean | undefined {
return this.sortModel?.dir === TableSortDirection.DESC;
get sortDesc(): boolean[] | boolean | undefined {
const s = this.sortModel.map((s) => s.dir === TableSortDirection.DESC);
return s.length <= 1 ? s[0] : s;
}
set sortDesc(value: boolean | undefined) {
this.sortModel = this.sortModel?.col
? {
col: this.sortModel.col,
dir: value ? TableSortDirection.DESC : TableSortDirection.ASC,
}
: undefined;
set sortDesc(value: boolean[] | boolean | undefined) {
this.sortModel = this.sortModel.map((s, index) => {
const sortDesc = Array.isArray(value) ? value[index] : value;
return {
col: s.col,
dir: sortDesc ? TableSortDirection.DESC : TableSortDirection.ASC,
};
});
}
get sortModel(): TableSort | undefined {
if (this.querySort) {
const sort = typeof this.sort === "string" ? this.sort : "";
const sortArgs = sort.split(",");
const col = sortArgs[0];
const dir = getSortDir(sortArgs[1]);
return col && dir ? { col, dir } : undefined;
get sortModel(): TableSort[] {
if (this.sort) {
return _castArray(this.sort).map((s) => {
if (typeof s !== "string") return s;
const sortArgs = s.split(",");
return {
col: sortArgs[0],
dir: sortArgs[1] as TableSortDirection,
};
});
}
return typeof this.sort === "string" ? undefined : this.sort;
return [];
}
set sortModel(value) {
set sortModel(value: TableSort[]) {
const sort = this.querySort
? value
? [value.col, value.dir].join(",")
: undefined
? value.map((s) => [s.col, s.dir].join(","))
: value;
this.$emit("update:sort", sort);
this.$emit("update:sort", sort.length <= 1 ? sort[0] : sort);
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/>
</div>
<search-field :debounce="0" v-model="search" />
<sortable-data-table
<iris-data-table
v-model="selection"
class="mt-5"
:headers="tableHeaders"
Expand All @@ -33,7 +33,7 @@
{{ item.vaccinationStatus }}
</v-chip>
</template>
</sortable-data-table>
</iris-data-table>
<error-message-alert :errors="[vrApi.state.error]" />
</v-card-text>
<v-card-actions>
Expand All @@ -52,7 +52,6 @@
import { Component, Mixins } from "vue-property-decorator";
import DataQueryHandler from "@/components/pageable/data-query-handler.vue";
import SearchField from "@/components/pageable/search-field.vue";
import SortableDataTable from "@/components/sortable-data-table.vue";
import ErrorMessageAlert from "@/components/error-message-alert.vue";
import { vaccinationReportApi } from "@/modules/vaccination-report/services/api";
import { VaccinationStatus, VREmployee } from "@/api";
Expand Down Expand Up @@ -85,7 +84,6 @@ export type VREmployeeTableRow = {
InfoGrid,
IrisDataTable,
ErrorMessageAlert,
SortableDataTable,
SearchField,
DataQueryHandler,
},
Expand Down
17 changes: 12 additions & 5 deletions iris-client-fe/src/server/utils/pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DataQuery } from "@/api/common";
import { DEFAULT_PAGE_SIZE } from "@/utils/pagination";
import _orderBy from "lodash/orderBy";
import _get from "lodash/get";
import _castArray from "lodash/castArray";

export enum TableSortDirection {
ASC = "asc",
Expand Down Expand Up @@ -42,11 +43,17 @@ export const queriedPage = <T, Q extends Partial<DataQuery>>(
const { page, size, sort, search, ...filters } = query;
const qPage = Number(page || 0);
const qSize = Number(size || DEFAULT_PAGE_SIZE);
const qSort = (sort || "").split(",");
const sortedItems =
qSort.length > 1
? _orderBy(items, [qSort[0]], [qSort[1] as TableSortDirection])
: items;
let sortedItems = items;
if (sort) {
const sortBy: string[] = [];
const sortOrder: string[] = [];
_castArray(sort).forEach((s) => {
const qSort = s.split(",");
sortBy.push(qSort[0]);
sortOrder.push(qSort[1]);
});
sortedItems = _orderBy(items, sortBy, sortOrder as TableSortDirection[]);
}
let filteredItems = sortedItems;
if (Object.keys(filters).length > 0) {
filteredItems = sortedItems.filter((item) => {
Expand Down
Loading

0 comments on commit e787248

Please sign in to comment.