Skip to content

Commit

Permalink
fix: correct sorting of numbers in list grid
Browse files Browse the repository at this point in the history
  • Loading branch information
kim.tran committed Oct 26, 2023
1 parent 48458d3 commit 50ab81d
Show file tree
Hide file tree
Showing 14 changed files with 1,693 additions and 1,319 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ export class DataListGridSortingComponent implements OnInit {
set sortDirection(value: DataSortDirection) {
this._sortDirection$.next(value)
}
_sortField = new BehaviorSubject<string>('')
_sortField$ = new BehaviorSubject<string>('')
@Input()
get sortField(): string {
return this?._sortField.getValue()
return this?._sortField$.getValue()
}
set sortField(value: string) {
this._sortField.next(value)
this._sortField$.next(value)
}

@Output() sortChange: EventEmitter<string> = new EventEmitter()
Expand All @@ -41,7 +41,7 @@ export class DataListGridSortingComponent implements OnInit {
}

selectSorting(event: any): void {
this._sortField.next(event.value)
this._sortField$.next(event.value)
this.sortChange.emit(event.value.columnId)
}
sortDirectionChanged(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<p-dataView
[value]="data"
[sortField]="clientSideSorting ? sortField : ''"
[sortOrder]="clientSideSorting ? sortDirectionNumber : 0"
[value]="(displayedItems$ | async) || []"
[paginator]="paginator"
[rows]="pageSize"
[layout]="layout"
[rowsPerPageOptions]="pageSizes"
id="dataListGrid_{{name}}"
>
<ng-template #gridItem let-item pTemplate="gridItem">
<ng-container
Expand All @@ -21,7 +20,7 @@
></ng-container>
</ng-template>
<ng-template pTemplate="empty">
<span>{{ emptyResultsMessage || ("DATA_LIST_GRID.EMPTY_RESULT" | translate) }}</span>
<span>{{ emptyResultsMessage || ("OCX_DATA_LIST_GRID.EMPTY_RESULT" | translate) }}</span>
</ng-template>
</p-dataView>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Inject,
Injector,
Input,
LOCALE_ID,
OnInit,
Output,
TemplateRef,
Expand All @@ -18,7 +19,11 @@ import { MfeInfo } from '../../../model/mfe-info.model'
import { DataAction } from '../../../model/data-action'
import { TranslateService } from '@ngx-translate/core'
import { ObjectUtils } from '../../utils/objectutils'
import { Row } from '../data-table/data-table.component'
import { Filter, Row } from '../data-table/data-table.component'
import { DataTableColumn } from '../../../model/data-table-column.model'
import { BehaviorSubject, combineLatest, map, mergeMap, Observable } from 'rxjs'
import { DataSortBase } from '../data-sort-base/data-sort-base'
import { Router } from '@angular/router'

export type ListGridData = {
id: string | number
Expand All @@ -37,11 +42,11 @@ export interface ListGridDataMenuItem extends MenuItem {
templateUrl: './data-list-grid.component.html',
styleUrls: ['./data-list-grid.component.scss'],
})
export class DataListGridComponent implements OnInit, DoCheck {
@Input() sortField = ''
export class DataListGridComponent extends DataSortBase implements OnInit, DoCheck {
@Input() titleLineId: string | undefined
@Input() subtitleLineIds: string[] = []
@Input() clientSideSorting = true
@Input() clientSideFiltering = true
@Input() sortStates: DataSortDirection[] = []
@Input() pageSizes: number[] = [10, 25, 50]
@Input() pageSize: number = this.pageSizes[0] || 50
Expand All @@ -55,28 +60,45 @@ export class DataListGridComponent implements OnInit, DoCheck {
@Input() editMenuItemKey: string | undefined
@Input() deleteMenuItemKey: string | undefined
@Input() paginator = true
@Input() columns: DataTableColumn[] = []
@Input() name = ''

_data$ = new BehaviorSubject<RowListGridData[]>([])
@Input()
get data(): RowListGridData[] {
return this._data$.getValue()
}
set data(value: RowListGridData[]) {
this._originalData = [...value]
this._data$.next([...value])
}
_filters$ = new BehaviorSubject<Filter[]>([])
@Input()
get filters(): Filter[] {
return this._filters$.getValue()
}
set filters(value: Filter[]) {
this._filters$.next(value)
}
_originalData: RowListGridData[] = []
_sortDirection = DataSortDirection.NONE

_sortDirection$ = new BehaviorSubject<DataSortDirection>(DataSortDirection.NONE)
@Input()
get sortDirection(): DataSortDirection {
return this._sortDirection
return this._sortDirection$.getValue()
}
set sortDirection(value: DataSortDirection) {
if (value === DataSortDirection.NONE) {
this._data = [...this._originalData]
this._data$.next([...this._originalData])
}
this._sortDirection = value
this._sortDirection$.next(value)
}
_data: RowListGridData[] = []
_sortField$ = new BehaviorSubject<string>('')
@Input()
get data(): RowListGridData[] {
return this._data
get sortField(): string {
return this?._sortField$.getValue()
}
set data(value: RowListGridData[]) {
this._originalData = [...value]
this._data = [...value]
set sortField(value: string) {
this._sortField$.next(value)
}

@Input() gridItemSubtitleLinesTemplate: TemplateRef<any> | undefined
Expand Down Expand Up @@ -118,35 +140,43 @@ export class DataListGridComponent implements OnInit, DoCheck {
@Output() deleteItem = new EventEmitter<ListGridData>()

get viewItemObserved(): boolean {
const dv = this.injector.get('DataViewComponent')
const dv = this.injector.get('DataViewComponent', null)
return dv?.viewItemObserved || dv?.viewItem.observed || this.viewItem.observed
}
get editItemObserved(): boolean {
const dv = this.injector.get('DataViewComponent')
const dv = this.injector.get('DataViewComponent', null)
return dv?.editItemObserved || dv?.editItem.observed || this.editItem.observed
}
get deleteItemObserved(): boolean {
const dv = this.injector.get('DataViewComponent')
const dv = this.injector.get('DataViewComponent', null)
return dv?.deleteItemObserved || dv?.deleteItem.observed || this.deleteItem.observed
}

showMenu = false
gridMenuItems: MenuItem[] = []
selectedItem: ListGridData | undefined
observedOutputs = 0

get sortDirectionNumber() {
get sortDirectionNumber(): number {
if (this.sortDirection === DataSortDirection.ASCENDING) return 1
if (this.sortDirection === DataSortDirection.DESCENDING) return -1
return 0
}

showMenu = false
gridMenuItems: MenuItem[] = []
selectedItem: ListGridData | undefined
observedOutputs = 0

displayedItems$: Observable<unknown[]> | undefined

constructor(
@Inject(LOCALE_ID) locale: string,
@Inject(AUTH_SERVICE) private authService: IAuthService,
@Inject(MFE_INFO) private mfeInfo: MfeInfo,
private translateService: TranslateService,
translateService: TranslateService,
private router: Router,
private injector: Injector
) {}
) {
super(locale, translateService)
this.name = this.name || this.router.url.replace(/[^A-Za-z0-9]/, '_')

}

ngDoCheck(): void {
const observedOutputs = <any>this.viewItem.observed + <any>this.deleteItem.observed + <any>this.editItem.observed
Expand All @@ -157,6 +187,13 @@ export class DataListGridComponent implements OnInit, DoCheck {
}

ngOnInit(): void {
this.displayedItems$ = combineLatest([this._data$, this._filters$, this._sortField$, this._sortDirection$]).pipe(
mergeMap((params) => this.translateItems(params, this.columns, this.clientSideFiltering, this.clientSideSorting)),
map((params) => this.filterItems(params, this.clientSideFiltering)),
map((params) => this.sortItems(params, this.columns, this.clientSideSorting)),
map(([items]) => this.flattenItems(items))
)

this.showMenu =
(!!this.viewPermission && this.authService.hasPermission(this.viewPermission)) ||
(!!this.editPermission && this.authService.hasPermission(this.editPermission)) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { TranslateService } from '@ngx-translate/core'
import { Observable, map, of } from 'rxjs'
import { flattenObject } from '../../../functions/flatten-object'
import { ColumnType } from '../../../model/column-type.model'
import { DataSortDirection } from '../../../model/data-sort-direction'
import { DataTableColumn } from '../../../model/data-table-column.model'
import { ListGridData } from '../../components/data-list-grid/data-list-grid.component'
import { Row, Filter } from '../../components/data-table/data-table.component'
import { ObjectUtils } from '../../utils/objectutils'

type RowListGridData = ListGridData | Row

export class DataSortBase {
constructor(protected locale: string, protected translateService: TranslateService) {}

translateItems(
[items, filters, sortColumn, sortDirection]: [
RowListGridData[],
Filter[],
string,
DataSortDirection,
],
columns: DataTableColumn[],
clientSideFiltering: boolean,
clientSideSorting: boolean
): Observable<[RowListGridData[], Filter[], string, DataSortDirection, Record<string, Record<string, string>>]> {
if (clientSideFiltering || clientSideSorting) {
let translationKeys: string[] = []
const translatedColumns = columns.filter((c) => c.columnType === ColumnType.TRANSLATION_KEY)
translatedColumns.forEach((c) => {
translationKeys = [...translationKeys, ...items.map((i) => ObjectUtils.resolveFieldData(i, c.id)?.toString())]
})
if (translationKeys.length) {
return this.translateService.get(translationKeys).pipe(
map((translatedValues: Record<string, string>) => {
const translations: Record<string, Record<string, string>> = {}
translatedColumns.forEach((c) => {
translations[c.id] = Object.fromEntries(
items.map((i) => [
ObjectUtils.resolveFieldData(i, c.id)?.toString() || '',
translatedValues[ObjectUtils.resolveFieldData(i, c.id)?.toString()],
])
)
})
return [items, filters, sortColumn, sortDirection, translations]
})
)
}
}
return of([items, filters, sortColumn, sortDirection, {}])
}

filterItems(
[items, filters, sortColumn, sortDirection, translations]: [
RowListGridData[],
Filter[],
string,
DataSortDirection,
Record<string, Record<string, string>>
],
clientSideFiltering: boolean
): [RowListGridData[], Filter[], string, DataSortDirection, Record<string, Record<string, string>>] {
if (!clientSideFiltering) {
return [items, filters, sortColumn, sortDirection, translations]
}
return [
items.filter((item) =>
filters
.map((filter) => filter.columnId)
.filter((value, index, self) => self.indexOf(value) === index && value != null)
.every((filterColumnId) =>
filters
.filter((filter) => filter.columnId === filterColumnId)
.some(
(filter) =>
(
translations[filter.columnId]?.[ObjectUtils.resolveFieldData(item, filter.columnId).toString()] ||
ObjectUtils.resolveFieldData(item, filter.columnId)
).toString() === filter.value.toString()
)
)
),
filters,
sortColumn,
sortDirection,
translations,
]
}

sortItems(
[items, filters, sortColumn, sortDirection, translations]: [
RowListGridData[],
Filter[],
string,
DataSortDirection,
Record<string, Record<string, string>>
],
columns: DataTableColumn[],
clientSideSorting: boolean
): [RowListGridData[], Filter[], string, DataSortDirection, Record<string, Record<string, string>>] {
if (!clientSideSorting || sortColumn === '') {
return [items, filters, sortColumn, sortDirection, translations]
}
let translatedColValues: Record<string, string> = Object.fromEntries(
items.map((i) => [
ObjectUtils.resolveFieldData(i, sortColumn)?.toString(),
ObjectUtils.resolveFieldData(i, sortColumn)?.toString(),
])
)
if (columns.find((h) => h.id === sortColumn)?.columnType === ColumnType.TRANSLATION_KEY) {
translatedColValues = translations[sortColumn]
}
return [
[...items].sort(this.createCompareFunction(translatedColValues, sortColumn, sortDirection)),
filters,
sortColumn,
sortDirection,
translations,
]
}

flattenItems(items: RowListGridData[]) {
return items.map((i) => flattenObject(i))
}

createCompareFunction(
translatedColValues: Record<string, string>,
sortColumn: string,
sortDirection: DataSortDirection,
): (a: Record<string, any>, b: Record<string, any>) => number {
let direction = 0
if (sortDirection === DataSortDirection.ASCENDING) {
direction = 1
} else if (sortDirection === DataSortDirection.DESCENDING) {
direction = -1
}
return (data1, data2) => {
if (direction === 0) {
return 0
}
let result
const value1 = translatedColValues[ObjectUtils.resolveFieldData(data1, sortColumn)]
const value2 = translatedColValues[ObjectUtils.resolveFieldData(data2, sortColumn)]

if (value1 == null && value2 != null) result = -1
else if (value1 != null && value2 == null) result = 1
else if (value1 == null && value2 == null) result = 0
else if (typeof value1 === 'string' && typeof value2 === 'string')
result = value1.localeCompare(value2, [this.locale, 'en', 'de'], { numeric: true })
else {
if (value1 < value2) {
result = -1
} else if (value1 > value2) {
result = 1
} else {
result = 0
}
}
return direction * result
}
}
}
Loading

0 comments on commit 50ab81d

Please sign in to comment.