[ADF-1404] Data Column enhancements for Document List (#2220)

* support 'timeAgo' format for data-column

* file size column type and bug fixes

* readme updates

* location column type

* readme fixes

* update unit tests

* file size pipe tests
This commit is contained in:
Denys Vuika 2017-08-16 09:53:39 +01:00 committed by Mario Romano
parent 9e5b19e34c
commit 06e03ad1e9
14 changed files with 295 additions and 50 deletions

View File

@ -99,11 +99,25 @@
class="image-table-cell"> class="image-table-cell">
</data-column> </data-column>
<data-column <data-column
title="{{'DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}"
key="name" key="name"
title="{{'DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}"
[formatTooltip]="getNodeNameTooltip" [formatTooltip]="getNodeNameTooltip"
class="full-width ellipsis-cell"> class="full-width ellipsis-cell">
</data-column> </data-column>
<!-- Location column demo -->
<!--
<data-column
key="path"
type="location"
format="/files"
title="Location">
</data-column>
-->
<data-column
key="content.sizeInBytes"
title="Size"
type="fileSize">
</data-column>
<!-- Notes: has performance overhead due to multiple files/folders causing separate HTTP calls to get tags --> <!-- Notes: has performance overhead due to multiple files/folders causing separate HTTP calls to get tags -->
<!-- <!--
<data-column <data-column
@ -124,11 +138,8 @@
title="{{'DOCUMENT_LIST.COLUMNS.CREATED' | translate}}" title="{{'DOCUMENT_LIST.COLUMNS.CREATED' | translate}}"
key="createdAt" key="createdAt"
type="date" type="date"
format="medium" format="timeAgo"
class="desktop-only"> class="desktop-only">
<ng-template let-value="value">
<span title="{{ value }}">{{ value | adfTimeAgo }}</span>
</ng-template>
</data-column> </data-column>
</data-columns> </data-columns>

View File

@ -126,6 +126,7 @@ export { ContextMenuModule } from './src/components/context-menu/context-menu.mo
export { CardViewModule } from './src/components/view/card-view.module'; export { CardViewModule } from './src/components/view/card-view.module';
export { CollapsableModule } from './src/components/collapsable/collapsable.module'; export { CollapsableModule } from './src/components/collapsable/collapsable.module';
export { CardViewItem } from './src/interface/card-view-item.interface'; export { CardViewItem } from './src/interface/card-view-item.interface';
export { TimeAgoPipe } from './src/pipes/time-ago.pipe';
export * from './src/components/data-column/data-column.component'; export * from './src/components/data-column/data-column.component';
export * from './src/components/data-column/data-column-list.component'; export * from './src/components/data-column/data-column-list.component';

View File

@ -0,0 +1,75 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FileSizePipe } from './file-size.pipe';
describe('FileSizePipe', () => {
let pipe: FileSizePipe;
beforeEach(() => {
pipe = new FileSizePipe();
});
it('returns empty string with invalid input', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
});
it('should convert value to Bytes', () => {
expect(pipe.transform(0)).toBe('0 Bytes');
expect(pipe.transform(1023)).toBe('1023 Bytes');
});
it('should convert value to KB', () => {
expect(pipe.transform(1024)).toBe('1 KB');
expect(pipe.transform(1048575)).toBe('1024 KB');
});
it('should convert value to MB', () => {
expect(pipe.transform(1048576)).toBe('1 MB');
expect(pipe.transform(1073741823)).toBe('1024 MB');
});
it('should convert value to GB', () => {
expect(pipe.transform(1073741824)).toBe('1 GB');
expect(pipe.transform(1099511627775)).toBe('1024 GB');
});
it('should convert value to TB and PB', () => {
expect(pipe.transform(1099511627776)).toBe('1 TB');
expect(pipe.transform(1125899906842623)).toBe('1 PB');
});
it('should convert value with custom precision', () => {
const tests = [
{ size: 10, precision: 2, expectancy: '10 Bytes'},
{ size: 1023, precision: 1, expectancy: '1023 Bytes'},
{ size: 1025, precision: 2, expectancy: '1 KB'},
{ size: 1499, precision: 0, expectancy: '1.46 KB'},
{ size: 1999, precision: 0, expectancy: '1.95 KB'},
{ size: 2000, precision: 2, expectancy: '1.95 KB'},
{ size: 5000000, precision: 4, expectancy: '4.7684 MB'},
{ size: 12345678901234, precision: 3, expectancy: '11.228 TB'}
];
tests.forEach(({ size, precision, expectancy }) => {
expect(pipe.transform(size, precision)).toBe(expectancy);
});
});
});

View File

@ -22,14 +22,20 @@ import { Pipe, PipeTransform } from '@angular/core';
}) })
export class FileSizePipe implements PipeTransform { export class FileSizePipe implements PipeTransform {
transform(bytes: number = 0, decimals: number = 2): string { transform(bytes: number, decimals: number = 2): string {
if (bytes == null || bytes === undefined) {
return '';
}
if (bytes === 0) { if (bytes === 0) {
return '0 Bytes'; return '0 Bytes';
} }
const k = 1024, const k = 1024,
dm = decimals || 2, dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k)); i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
} }

View File

@ -16,6 +16,7 @@
*/ */
import { ModuleWithProviders, NgModule } from '@angular/core'; import { ModuleWithProviders, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CoreModule, TRANSLATION_PROVIDER } from 'ng2-alfresco-core'; import { CoreModule, TRANSLATION_PROVIDER } from 'ng2-alfresco-core';
import { MaterialModule } from './src/material.module'; import { MaterialModule } from './src/material.module';
@ -30,6 +31,7 @@ export { DataRowActionEvent, DataRowActionModel } from './src/components/datatab
import { DataTableCellComponent } from './src/components/datatable/datatable-cell.component'; import { DataTableCellComponent } from './src/components/datatable/datatable-cell.component';
import { DataTableComponent } from './src/components/datatable/datatable.component'; import { DataTableComponent } from './src/components/datatable/datatable.component';
import { EmptyListComponent } from './src/components/datatable/empty-list.component'; import { EmptyListComponent } from './src/components/datatable/empty-list.component';
import { LocationCellComponent } from './src/components/datatable/location-cell.component';
import { LoadingContentTemplateDirective } from './src/directives/loading-template.directive'; import { LoadingContentTemplateDirective } from './src/directives/loading-template.directive';
import { NoContentTemplateDirective } from './src/directives/no-content-template.directive'; import { NoContentTemplateDirective } from './src/directives/no-content-template.directive';
@ -38,6 +40,7 @@ export function directives() {
DataTableComponent, DataTableComponent,
EmptyListComponent, EmptyListComponent,
DataTableCellComponent, DataTableCellComponent,
LocationCellComponent,
NoContentTemplateDirective, NoContentTemplateDirective,
LoadingContentTemplateDirective LoadingContentTemplateDirective
]; ];
@ -45,6 +48,7 @@ export function directives() {
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule,
CoreModule, CoreModule,
MaterialModule MaterialModule
], ],
@ -61,7 +65,8 @@ export function directives() {
], ],
exports: [ exports: [
...directives(), ...directives(),
MaterialModule MaterialModule,
RouterModule
] ]
}) })
export class DataTableModule { export class DataTableModule {

View File

@ -37,10 +37,8 @@ export class DataTableCellComponent implements OnInit {
@Input() @Input()
value: any; value: any;
constructor() { }
ngOnInit() { ngOnInit() {
if (this.column && this.column.key && this.row && this.data) { if (!this.value && this.column && this.column.key && this.row && this.data) {
this.value = this.data.getValue(this.row, this.column); this.value = this.data.getValue(this.row, this.column);
} }
} }

View File

@ -84,6 +84,16 @@
[attr.data-automation-id]="'date_' + data.getValue(row, col)"> [attr.data-automation-id]="'date_' + data.getValue(row, col)">
<adf-datatable-cell [data]="data" [column]="col" [row]="row"></adf-datatable-cell> <adf-datatable-cell [data]="data" [column]="col" [row]="row"></adf-datatable-cell>
</div> </div>
<div *ngSwitchCase="'location'" class="cell-value"
[mdTooltip]="getCellTooltip(row, col)"
[attr.data-automation-id]="'location' + data.getValue(row, col)">
<adf-location-cell [data]="data" [column]="col" [row]="row"></adf-location-cell>
</div>
<div *ngSwitchCase="'fileSize'" class="cell-value"
[mdTooltip]="getCellTooltip(row, col)"
[attr.data-automation-id]="'fileSize_' + data.getValue(row, col)">
<adf-datatable-cell [value]="data.getValue(row, col) | adfFileSize"></adf-datatable-cell>
</div>
<div *ngSwitchCase="'text'" class="cell-value" <div *ngSwitchCase="'text'" class="cell-value"
[mdTooltip]="getCellTooltip(row, col)" [mdTooltip]="getCellTooltip(row, col)"
[attr.data-automation-id]="'text_' + data.getValue(row, col)"> [attr.data-automation-id]="'text_' + data.getValue(row, col)">

View File

@ -18,6 +18,7 @@
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MdCheckboxChange } from '@angular/material'; import { MdCheckboxChange } from '@angular/material';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { MaterialModule } from '../../material.module'; import { MaterialModule } from '../../material.module';
import { import {
@ -29,6 +30,7 @@ import {
} from './../../data/index'; } from './../../data/index';
import { DataTableCellComponent } from './datatable-cell.component'; import { DataTableCellComponent } from './datatable-cell.component';
import { DataTableComponent } from './datatable.component'; import { DataTableComponent } from './datatable.component';
import { LocationCellComponent } from './location-cell.component';
describe('DataTable', () => { describe('DataTable', () => {
@ -40,11 +42,13 @@ describe('DataTable', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule.forRoot(), RouterTestingModule,
CoreModule,
MaterialModule MaterialModule
], ],
declarations: [ declarations: [
DataTableCellComponent, DataTableCellComponent,
LocationCellComponent,
DataTableComponent DataTableComponent
] ]
}).compileComponents(); }).compileComponents();

View File

@ -0,0 +1,60 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ChangeDetectionStrategy, Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { PathInfoEntity } from 'alfresco-js-api';
import { DataTableCellComponent } from './datatable-cell.component';
@Component({
selector: 'adf-location-cell',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ng-container>
<a href="" [title]="tooltip" [routerLink]="link">
{{ displayText }}
</a>
</ng-container>
`,
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-location-cell' }
})
export class LocationCellComponent extends DataTableCellComponent implements OnInit {
@Input()
tooltip: string = '';
@Input()
link: any[];
@Input()
displayText: string = '';
/** @override */
ngOnInit() {
if (!this.value && this.column && this.column.key && this.row && this.data) {
const path: PathInfoEntity = this.data.getValue(this.row, this.column);
if (path) {
this.value = path;
this.displayText = path.name.split('/').pop();
this.tooltip = path.name;
const parent = path.elements[path.elements.length - 1];
this.link = [ this.column.format, parent.id ];
}
}
}
}

View File

@ -17,7 +17,8 @@
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { TemplateRef } from '@angular/core'; import { TemplateRef } from '@angular/core';
import { ObjectUtils } from 'ng2-alfresco-core';
import { ObjectUtils, TimeAgoPipe } from 'ng2-alfresco-core';
import { DataColumn, DataRow, DataSorting, DataTableAdapter } from './datatable-adapter'; import { DataColumn, DataRow, DataSorting, DataTableAdapter } from './datatable-adapter';
// Simple implementation of the DataTableAdapter interface. // Simple implementation of the DataTableAdapter interface.
@ -103,12 +104,10 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
let value = row.getValue(col.key); let value = row.getValue(col.key);
if (col.type === 'date') { if (col.type === 'date') {
let datePipe = new DatePipe('en-US');
let format = col.format || 'medium';
try { try {
return datePipe.transform(value, format); return this.formatDate(col, value);
} catch (err) { } catch (err) {
console.error(`DocumentList: error parsing date ${value} to format ${format}`); console.error(`Error parsing date ${value} to format ${col.format}`);
} }
} }
@ -120,6 +119,21 @@ export class ObjectDataTableAdapter implements DataTableAdapter {
return value; return value;
} }
formatDate(col: DataColumn, value: any): string {
if (col.type === 'date') {
const format = col.format || 'medium';
if (format === 'timeAgo') {
const timeAgoPipe = new TimeAgoPipe();
return timeAgoPipe.transform(value);
} else {
const datePipe = new DatePipe('en-US');
return datePipe.transform(value, format);
}
}
return value;
}
getSorting(): DataSorting { getSorting(): DataSorting {
return this._sorting; return this._sorting;
} }

View File

@ -16,9 +16,11 @@
*/ */
import { async, TestBed } from '@angular/core/testing'; import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { DataTableCellComponent } from '../components/datatable/datatable-cell.component'; import { DataTableCellComponent } from '../components/datatable/datatable-cell.component';
import { DataTableComponent } from '../components/datatable/datatable.component'; import { DataTableComponent } from '../components/datatable/datatable.component';
import { LocationCellComponent } from '../components/datatable/location-cell.component';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { LoadingContentTemplateDirective } from './loading-template.directive'; import { LoadingContentTemplateDirective } from './loading-template.directive';
@ -30,12 +32,14 @@ describe('LoadingContentTemplateDirective', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule,
MaterialModule, MaterialModule,
CoreModule.forRoot() CoreModule
], ],
declarations: [ declarations: [
DataTableComponent, DataTableComponent,
DataTableCellComponent, DataTableCellComponent,
LocationCellComponent,
LoadingContentTemplateDirective LoadingContentTemplateDirective
] ]
}).compileComponents(); }).compileComponents();

View File

@ -16,9 +16,11 @@
*/ */
import { async, TestBed } from '@angular/core/testing'; import { async, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule } from 'ng2-alfresco-core';
import { DataTableCellComponent } from '../components/datatable/datatable-cell.component'; import { DataTableCellComponent } from '../components/datatable/datatable-cell.component';
import { DataTableComponent } from '../components/datatable/datatable.component'; import { DataTableComponent } from '../components/datatable/datatable.component';
import { LocationCellComponent } from '../components/datatable/location-cell.component';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { NoContentTemplateDirective } from './no-content-template.directive'; import { NoContentTemplateDirective } from './no-content-template.directive';
@ -30,13 +32,15 @@ describe('NoContentTemplateDirective', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule,
MaterialModule, MaterialModule,
CoreModule.forRoot() CoreModule
], ],
declarations: [ declarations: [
DataTableComponent, DataTableComponent,
DataTableCellComponent, DataTableCellComponent,
NoContentTemplateDirective NoContentTemplateDirective,
LocationCellComponent
] ]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -441,7 +441,7 @@ Here's the list of available properties you can define for a Data Column definit
| Name | Type | Default | Description | | Name | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| key | string | | Data source key, can be either column/property key like `title` or property path like `createdBy.name` | | key | string | | Data source key, can be either column/property key like `title` or property path like `createdBy.name` |
| type | string (text\|image\|date) | text | Value type | | type | string | text | Value type |
| format | string | | Value format (if supported by components), for example format of the date | | format | string | | Value format (if supported by components), for example format of the date |
| sortable | boolean | true | Toggles ability to sort by this column, for example by clicking the column header | | sortable | boolean | true | Toggles ability to sort by this column, for example by clicking the column header |
| title | string | | Display title of the column, typically used for column headers | | title | string | | Display title of the column, typically used for column headers |
@ -450,6 +450,18 @@ Here's the list of available properties you can define for a Data Column definit
| class | string | | Additional CSS class to be applied to column (header and cells) | | class | string | | Additional CSS class to be applied to column (header and cells) |
| formatTooltip | Function | | Custom tooltip formatter function. | | formatTooltip | Function | | Custom tooltip formatter function. |
### Column Types
The DataColumn `type` property can take one of the following values:
- text
- image
- date
- fileSize
- location
### Underlying node object
DocumentList component assigns an instance of `MinimalNode` type (`alfresco-js-api`) as a data context of each row. DocumentList component assigns an instance of `MinimalNode` type (`alfresco-js-api`) as a data context of each row.
```js ```js
@ -493,24 +505,54 @@ Here's a short example:
</adf-document-list> </adf-document-list>
``` ```
## Column definition ### Date Column
Properties:
| Name | Type | Default | Description
| --- | --- | --- | --- |
| title | string | | Column title |
| sr-title | string | | Screen reader title, used only when `title` is empty |
| key | string | | Column source key, example: `createdByUser.displayName` |
| sortable | boolean | false | Toggle sorting ability via column header clicks |
| class | string | | CSS class list, example: `full-width ellipsis-cell` |
| type | string | text | Column type, text\|date\|number |
| format | string | | Value format pattern |
| template | `TemplateRef<any>` | | Column template |
For `date` column type the [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) formatting is used. For `date` column type the [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) formatting is used.
For a full list of available `format` values please refer to [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) documentation. For a full list of available `format` values please refer to [DatePipe](https://angular.io/docs/ts/latest/api/common/DatePipe-class.html) documentation.
ADF also supports additional `timeAgo` value for the `format` property.
That triggers the date values to be rendered using popular ["Time from now"](https://momentjs.com/docs/#/displaying/fromnow/) format.
### Location Column
This column is used to display a clickable location link pointing to the parent path of the node.
You are going to use it with custom navigation or when displaying content from the sources like:
- Sites
- Shared Links
- Recent Files
- Favorites
- Trashcan
or any other location that needs nagivating to the node parent folder easily.
Note that the parent node is evaluated automatically,
the generated link will be pointing to URL based on the `format` property value with the node `id` value appended:
```text
/<format>/:id
```
For example:
```html
<data-column
key="path"
type="location"
format="/files"
title="Location">
</data-column>
```
All links rendered in the column above will have an address mapped to `/files`:
```text
/files/node-1-id
/files/node-2-id
...
```
### Column Template ### Column Template
It is possible to provide custom column/cell template that may contain other Angular components or HTML elements: It is possible to provide custom column/cell template that may contain other Angular components or HTML elements:

View File

@ -17,7 +17,7 @@
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { MinimalNode, MinimalNodeEntity, NodePaging } from 'alfresco-js-api'; import { MinimalNode, MinimalNodeEntity, NodePaging } from 'alfresco-js-api';
import { ObjectUtils } from 'ng2-alfresco-core'; import { ObjectUtils, TimeAgoPipe } from 'ng2-alfresco-core';
import { DataColumn, DataRow, DataSorting, DataTableAdapter } from 'ng2-alfresco-datatable'; import { DataColumn, DataRow, DataSorting, DataTableAdapter } from 'ng2-alfresco-datatable';
import { PermissionStyleModel } from './../models/permissions-style.model'; import { PermissionStyleModel } from './../models/permissions-style.model';
import { DocumentListService } from './../services/document-list.service'; import { DocumentListService } from './../services/document-list.service';
@ -27,8 +27,6 @@ export class ShareDataTableAdapter implements DataTableAdapter {
ERR_ROW_NOT_FOUND: string = 'Row not found'; ERR_ROW_NOT_FOUND: string = 'Row not found';
ERR_COL_NOT_FOUND: string = 'Column not found'; ERR_COL_NOT_FOUND: string = 'Column not found';
DEFAULT_DATE_FORMAT: string = 'medium';
private sorting: DataSorting; private sorting: DataSorting;
private rows: DataRow[]; private rows: DataRow[];
private columns: DataColumn[]; private columns: DataColumn[];
@ -81,13 +79,11 @@ export class ShareDataTableAdapter implements DataTableAdapter {
} }
if (col.type === 'date') { if (col.type === 'date') {
let datePipe = new DatePipe('en-US');
let format = col.format || this.DEFAULT_DATE_FORMAT;
try { try {
let result = datePipe.transform(value, format); const result = this.formatDate(col, value);
return dataRow.cacheValue(col.key, result); return dataRow.cacheValue(col.key, result);
} catch (err) { } catch (err) {
console.error(`Error parsing date ${value} to format ${format}`); console.error(`Error parsing date ${value} to format ${col.format}`);
return 'Error'; return 'Error';
} }
} }
@ -129,6 +125,21 @@ export class ShareDataTableAdapter implements DataTableAdapter {
return dataRow.cacheValue(col.key, value); return dataRow.cacheValue(col.key, value);
} }
formatDate(col: DataColumn, value: any): string {
if (col.type === 'date') {
const format = col.format || 'medium';
if (format === 'timeAgo') {
const timeAgoPipe = new TimeAgoPipe();
return timeAgoPipe.transform(value);
} else {
const datePipe = new DatePipe('en-US');
return datePipe.transform(value, format);
}
}
return value;
}
getSorting(): DataSorting { getSorting(): DataSorting {
return this.sorting; return this.sorting;
} }