@sumaris-net/ngx-components 2.3.6 → 2.4.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/public_api.mjs +4 -1
- package/esm2020/src/app/admin/users/users.mjs +3 -2
- package/esm2020/src/app/core/table/async-table.class.mjs +1615 -0
- package/esm2020/src/app/core/table/entities-async-table-datasource.class.mjs +383 -0
- package/esm2020/src/app/core/table/table.class.mjs +2 -12
- package/esm2020/src/app/core/table/table.model.mjs +12 -0
- package/esm2020/src/app/core/table/testing/table.testing.mjs +3 -2
- package/esm2020/src/app/shared/material/datetime/material.datetime.mjs +2 -2
- package/fesm2015/sumaris-net.ngx-components.mjs +2091 -14
- package/fesm2015/sumaris-net.ngx-components.mjs.map +1 -1
- package/fesm2020/sumaris-net.ngx-components.mjs +1977 -14
- package/fesm2020/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +3 -0
- package/src/app/core/table/async-table.class.d.ts +368 -0
- package/src/app/core/table/entities-async-table-datasource.class.d.ts +79 -0
- package/src/app/core/table/table.class.d.ts +1 -22
- package/src/app/core/table/table.model.d.ts +23 -0
|
@@ -140,7 +140,7 @@ import { isDataSource, SelectionModel } from '@angular/cdk/collections';
|
|
|
140
140
|
import { Camera, CameraResultType } from '@capacitor/camera';
|
|
141
141
|
import { Geolocation } from '@capacitor/geolocation';
|
|
142
142
|
import * as i3$2 from '@e-is/ngx-material-table';
|
|
143
|
-
import { ValidatorService, TableDataSource } from '@e-is/ngx-material-table';
|
|
143
|
+
import { ValidatorService, TableDataSource, AsyncTableDataSource } from '@e-is/ngx-material-table';
|
|
144
144
|
|
|
145
145
|
const ENVIRONMENT = new InjectionToken('ENV');
|
|
146
146
|
class Environment {
|
|
@@ -6439,7 +6439,7 @@ class MatDateTime {
|
|
|
6439
6439
|
}
|
|
6440
6440
|
else {
|
|
6441
6441
|
// Reset hour
|
|
6442
|
-
const day = date.startOf('day');
|
|
6442
|
+
const day = date.clone().startOf('day');
|
|
6443
6443
|
const dayStr = this.dateAdapter.format(day, this.dayPattern);
|
|
6444
6444
|
// Format time
|
|
6445
6445
|
let timeStr;
|
|
@@ -25100,6 +25100,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
25100
25100
|
type: Directive
|
|
25101
25101
|
}], ctorParameters: function () { return [{ type: GraphqlService }, { type: PlatformService }, { type: undefined }, { type: undefined }, { type: undefined }]; } });
|
|
25102
25102
|
|
|
25103
|
+
const SETTINGS_DISPLAY_COLUMNS = 'displayColumns';
|
|
25104
|
+
const SETTINGS_SORTED_COLUMN = 'sortedColumn';
|
|
25105
|
+
const SETTINGS_FILTER = 'filter';
|
|
25106
|
+
const SETTINGS_PAGE_SIZE = 'pageSize';
|
|
25107
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
25108
|
+
const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100, 200, 500];
|
|
25109
|
+
const RESERVED_START_COLUMNS = ['select', 'id'];
|
|
25110
|
+
const RESERVED_END_COLUMNS = ['actions'];
|
|
25111
|
+
const DEFAULT_REQUIRED_COLUMNS = ['id'];
|
|
25112
|
+
class CellValueChangeListener {
|
|
25113
|
+
}
|
|
25114
|
+
|
|
25103
25115
|
class AppTableUtils {
|
|
25104
25116
|
static waitIdle(table) {
|
|
25105
25117
|
if (!table || !table.dataSource) {
|
|
@@ -25501,17 +25513,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
25501
25513
|
type: Directive
|
|
25502
25514
|
}], ctorParameters: function () { return [{ type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }]; } });
|
|
25503
25515
|
|
|
25504
|
-
const SETTINGS_DISPLAY_COLUMNS = 'displayColumns';
|
|
25505
|
-
const SETTINGS_SORTED_COLUMN = 'sortedColumn';
|
|
25506
|
-
const SETTINGS_FILTER = 'filter';
|
|
25507
|
-
const SETTINGS_PAGE_SIZE = 'pageSize';
|
|
25508
|
-
const DEFAULT_PAGE_SIZE = 20;
|
|
25509
|
-
const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100, 200, 500];
|
|
25510
|
-
const RESERVED_START_COLUMNS = ['select', 'id'];
|
|
25511
|
-
const RESERVED_END_COLUMNS = ['actions'];
|
|
25512
|
-
const DEFAULT_REQUIRED_COLUMNS = ['id'];
|
|
25513
|
-
class CellValueChangeListener {
|
|
25514
|
-
}
|
|
25515
25516
|
// @dynamic
|
|
25516
25517
|
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
25517
25518
|
class AppTable {
|
|
@@ -28835,6 +28836,1968 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
28835
28836
|
type: Input
|
|
28836
28837
|
}] } });
|
|
28837
28838
|
|
|
28839
|
+
// noinspection DuplicatedCode
|
|
28840
|
+
// @dynamic
|
|
28841
|
+
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
28842
|
+
class EntitiesAsyncTableDataSource extends AsyncTableDataSource {
|
|
28843
|
+
/**
|
|
28844
|
+
* Creates a new TableDataSource instance, that can be used as datasource of `@angular/cdk` data-table.
|
|
28845
|
+
*
|
|
28846
|
+
* @param dataService A service to load and save data
|
|
28847
|
+
* @param dataType Type of data contained by the Table. If not specified, then `data` with at least one element must be specified.
|
|
28848
|
+
* @param environment
|
|
28849
|
+
* @param validatorService Service that create instances of the FormGroup used to validate row fields.
|
|
28850
|
+
* @param config Additional configuration for table.
|
|
28851
|
+
*/
|
|
28852
|
+
constructor(dataType, dataService, validatorService, options) {
|
|
28853
|
+
super([], dataType, validatorService, {
|
|
28854
|
+
keepOriginalDataAfterConfirm: false,
|
|
28855
|
+
readOnly: false,
|
|
28856
|
+
saveOnlyDirtyRows: false,
|
|
28857
|
+
...options
|
|
28858
|
+
});
|
|
28859
|
+
this.dataService = dataService;
|
|
28860
|
+
this._debug = false;
|
|
28861
|
+
this._creating = false;
|
|
28862
|
+
this._saving = false;
|
|
28863
|
+
this._fetchMoreFn = null;
|
|
28864
|
+
this._stopWatchSubject = new Subject();
|
|
28865
|
+
this.loadingSubject = new BehaviorSubject(undefined);
|
|
28866
|
+
this._entityName = removeEnd((new dataType()).__typename || 'UnknownVO', 'VO');
|
|
28867
|
+
this._debug = options?.suppressErrors === false && !environment.production;
|
|
28868
|
+
}
|
|
28869
|
+
get watchAllOptions() {
|
|
28870
|
+
return this.config.watchAllOptions;
|
|
28871
|
+
}
|
|
28872
|
+
set watchAllOptions(value) {
|
|
28873
|
+
this.config.watchAllOptions = value;
|
|
28874
|
+
}
|
|
28875
|
+
get saveAllOptions() {
|
|
28876
|
+
return this.config.saveAllOptions;
|
|
28877
|
+
}
|
|
28878
|
+
set saveAllOptions(value) {
|
|
28879
|
+
this.config.saveAllOptions = value;
|
|
28880
|
+
}
|
|
28881
|
+
get loaded() {
|
|
28882
|
+
return this.loadingSubject.value === false; // Should be false when undefined (initial state)
|
|
28883
|
+
}
|
|
28884
|
+
get loading() {
|
|
28885
|
+
return this.loadingSubject.value !== false; // Should be true when undefined (initial state)
|
|
28886
|
+
}
|
|
28887
|
+
ngOnDestroy() {
|
|
28888
|
+
this.disconnect();
|
|
28889
|
+
}
|
|
28890
|
+
watchAll(offset, size, sortBy, sortDirection, filter) {
|
|
28891
|
+
this._stopWatchSubject.next();
|
|
28892
|
+
this._fetchMoreFn = null;
|
|
28893
|
+
this.markAsLoading();
|
|
28894
|
+
return this.dataService.watchAll(offset, size, sortBy, sortDirection, filter, this.watchAllOptions)
|
|
28895
|
+
.pipe(catchError(err => this.handleError(err, 'ERROR.LOAD_DATA_ERROR')), map((res) => {
|
|
28896
|
+
if (this._saving) {
|
|
28897
|
+
console.info(`[entities-table-datasource] Received ${this._entityName} data (from service), but still saving: skip`);
|
|
28898
|
+
}
|
|
28899
|
+
else if (this.hasSomeEditingRow()) {
|
|
28900
|
+
console.warn(`[entities-table-datasource] Received ${this._entityName} data, while some row still editing: skip; Please check save() implementation in the table!`);
|
|
28901
|
+
}
|
|
28902
|
+
else {
|
|
28903
|
+
this.updateDatasource((res.data || []));
|
|
28904
|
+
this._fetchMoreFn = res.fetchMore;
|
|
28905
|
+
}
|
|
28906
|
+
return res;
|
|
28907
|
+
}),
|
|
28908
|
+
// Stop this pipe next time we call watchAll()
|
|
28909
|
+
takeUntil(this._stopWatchSubject)
|
|
28910
|
+
// ⚠ Notice: Don't put any operator after takeUntil to avoid potential subscription leaks
|
|
28911
|
+
);
|
|
28912
|
+
}
|
|
28913
|
+
updateDatasourceFromRows(rows) {
|
|
28914
|
+
// Avoid to update dataSourceSubject, when not need
|
|
28915
|
+
if (this.datasourceSubject.observers?.length) {
|
|
28916
|
+
if (!this.config.suppressErrors)
|
|
28917
|
+
console.warn('[entities-table-datasource] Update datasource subject. Please prefer using \'rowsSubject\' instead of \'datasourceSubject\'');
|
|
28918
|
+
super.updateDatasourceFromRows(rows);
|
|
28919
|
+
}
|
|
28920
|
+
else {
|
|
28921
|
+
console.debug('[entities-table-datasource] Skipping datasourceSubject update (not used yet).');
|
|
28922
|
+
}
|
|
28923
|
+
}
|
|
28924
|
+
async save() {
|
|
28925
|
+
if (this.config.readOnly) {
|
|
28926
|
+
console.error('[entities-table-datasource] Enable to save, because config.readOnly=true');
|
|
28927
|
+
return false;
|
|
28928
|
+
}
|
|
28929
|
+
// Saving twice (should never occur)
|
|
28930
|
+
if (this._saving) {
|
|
28931
|
+
console.warn(`[entities-table-datasource] Trying to save ${this._entityName} rows twice. Skip`);
|
|
28932
|
+
return false;
|
|
28933
|
+
}
|
|
28934
|
+
this._saving = true;
|
|
28935
|
+
this.markAsLoading();
|
|
28936
|
+
const onlyDirtyRows = this.config.saveOnlyDirtyRows;
|
|
28937
|
+
try {
|
|
28938
|
+
if (this._debug)
|
|
28939
|
+
console.debug(`[entities-table-datasource] Saving ${this._entityName} rows... {onlyDirtyRows: ${onlyDirtyRows}}`);
|
|
28940
|
+
// Get all rows
|
|
28941
|
+
const rows = this.getRows();
|
|
28942
|
+
// Finish editing all rows
|
|
28943
|
+
const invalidRows = (await Promise.all(this.getEditingRows()
|
|
28944
|
+
.map(row => row.confirmEditCreate().then(confirmed => confirmed === false ? row : null))))
|
|
28945
|
+
.filter(isNotNil);
|
|
28946
|
+
// Cannot finish some rows: error
|
|
28947
|
+
if (invalidRows.length) {
|
|
28948
|
+
// log errors
|
|
28949
|
+
if (this._debug)
|
|
28950
|
+
invalidRows.forEach(row => AppTableUtils.logRowErrors(row, `[entities-table-datasource] ${this._entityName} row #${row.id}`));
|
|
28951
|
+
// Stop with an error
|
|
28952
|
+
throw { code: ErrorCodes.TABLE_INVALID_ROW_ERROR, message: 'ERROR.TABLE_INVALID_ROW_ERROR' };
|
|
28953
|
+
}
|
|
28954
|
+
let data;
|
|
28955
|
+
let dataToSave;
|
|
28956
|
+
if (this.validatorService) {
|
|
28957
|
+
dataToSave = [];
|
|
28958
|
+
data = rows.map(row => {
|
|
28959
|
+
const currentData = new this.dataConstructor();
|
|
28960
|
+
currentData.fromObject(row.currentData);
|
|
28961
|
+
// Filter to keep only dirty row
|
|
28962
|
+
if (onlyDirtyRows && row.validator.dirty)
|
|
28963
|
+
dataToSave.push(currentData);
|
|
28964
|
+
return currentData;
|
|
28965
|
+
});
|
|
28966
|
+
if (!onlyDirtyRows)
|
|
28967
|
+
dataToSave = data;
|
|
28968
|
+
}
|
|
28969
|
+
// Or use the current data without conversion (when no validator service used)
|
|
28970
|
+
else {
|
|
28971
|
+
data = rows.map(row => row.currentData);
|
|
28972
|
+
// save all data, as we don't have any dirty marker
|
|
28973
|
+
dataToSave = data;
|
|
28974
|
+
}
|
|
28975
|
+
// If no data to save: exit
|
|
28976
|
+
if (onlyDirtyRows && !dataToSave.length) {
|
|
28977
|
+
if (this._debug)
|
|
28978
|
+
console.debug(`[entities-table-datasource] No ${this._entityName} data to save. Skip`);
|
|
28979
|
+
return false;
|
|
28980
|
+
}
|
|
28981
|
+
if (this._debug)
|
|
28982
|
+
console.debug(`[entities-table-datasource] Asking service to save this ${this._entityName} data:`, dataToSave);
|
|
28983
|
+
await this.dataService.saveAll(dataToSave, this.saveAllOptions);
|
|
28984
|
+
if (this._debug)
|
|
28985
|
+
console.debug(`[entities-table-datasource] Saving ${this._entityName} data [OK]`);
|
|
28986
|
+
// LP 23/03/2021: update datasource is necessary but can be changed to a refetch() on QueryRef (must be created and registered in GraphqlService.watchQuery)
|
|
28987
|
+
this.updateDatasource(data, { emitEvent: false });
|
|
28988
|
+
return true;
|
|
28989
|
+
}
|
|
28990
|
+
catch (error) {
|
|
28991
|
+
if (this._debug)
|
|
28992
|
+
console.error('[entities-table-datasource] Error while saving: ' + error && error.message || error);
|
|
28993
|
+
throw error;
|
|
28994
|
+
}
|
|
28995
|
+
finally {
|
|
28996
|
+
this._saving = false;
|
|
28997
|
+
this.markAsLoaded();
|
|
28998
|
+
}
|
|
28999
|
+
}
|
|
29000
|
+
updateDatasource(data, opts) {
|
|
29001
|
+
if (this._debug)
|
|
29002
|
+
console.debug(`[entities-table-datasource] Updating datasource with data:`, data);
|
|
29003
|
+
super.updateDatasource(data, opts);
|
|
29004
|
+
if (!opts || opts.emitEvent !== false) {
|
|
29005
|
+
this.markAsLoaded();
|
|
29006
|
+
}
|
|
29007
|
+
}
|
|
29008
|
+
connect(collectionViewer) {
|
|
29009
|
+
// DEBUG
|
|
29010
|
+
//console.debug("[entities-datasource] connect");
|
|
29011
|
+
return super.connect(collectionViewer);
|
|
29012
|
+
}
|
|
29013
|
+
disconnect(collectionViewer) {
|
|
29014
|
+
if (this._debug)
|
|
29015
|
+
console.debug('[entities-table-datasource] Disconnecting...');
|
|
29016
|
+
super.disconnect(collectionViewer);
|
|
29017
|
+
if (!this._stopWatchSubject.closed) {
|
|
29018
|
+
if (this._debug)
|
|
29019
|
+
console.debug('[entities-table-datasource] Closing...');
|
|
29020
|
+
this._stopWatchSubject.next();
|
|
29021
|
+
this._stopWatchSubject.complete();
|
|
29022
|
+
this._stopWatchSubject.unsubscribe();
|
|
29023
|
+
this.loadingSubject.complete();
|
|
29024
|
+
this.loadingSubject.unsubscribe();
|
|
29025
|
+
}
|
|
29026
|
+
}
|
|
29027
|
+
waitIdle(debounceTimeMs) {
|
|
29028
|
+
return firstFalsePromise(this.loadingSubject
|
|
29029
|
+
.asObservable()
|
|
29030
|
+
.pipe(debounceTime(debounceTimeMs || 100) // if not started yet, wait
|
|
29031
|
+
));
|
|
29032
|
+
}
|
|
29033
|
+
async confirmCreate(row) {
|
|
29034
|
+
const confirmed = await super.confirmCreate(row);
|
|
29035
|
+
if (!confirmed)
|
|
29036
|
+
return false;
|
|
29037
|
+
if (row.editing && row.validator) {
|
|
29038
|
+
console.warn('[entities-table-datasource] Row still has {editing: true} after confirmCreate()! Force editing to false');
|
|
29039
|
+
row.validator.disable({ onlySelf: true, emitEvent: false });
|
|
29040
|
+
}
|
|
29041
|
+
return confirmed;
|
|
29042
|
+
}
|
|
29043
|
+
async confirmEdit(row) {
|
|
29044
|
+
const confirmed = await super.confirmEdit(row);
|
|
29045
|
+
if (!confirmed)
|
|
29046
|
+
return false;
|
|
29047
|
+
if (row.editing && row.validator) {
|
|
29048
|
+
console.warn('[entities-table-datasource] Row still has {editing: true} after confirmEdit()! Force editing to false');
|
|
29049
|
+
row.validator.disable({ onlySelf: true, emitEvent: false });
|
|
29050
|
+
}
|
|
29051
|
+
return true;
|
|
29052
|
+
}
|
|
29053
|
+
async startEdit(row) {
|
|
29054
|
+
const editing = await super.startEdit(row);
|
|
29055
|
+
if (!editing)
|
|
29056
|
+
return false;
|
|
29057
|
+
if (!row.editing && row.validator) {
|
|
29058
|
+
console.warn('[entities-table-datasource] Row still has {editing: false} after startEdit()! Force editing');
|
|
29059
|
+
row.validator.enable({ onlySelf: true, emitEvent: false });
|
|
29060
|
+
}
|
|
29061
|
+
return true;
|
|
29062
|
+
}
|
|
29063
|
+
handleError(error, message) {
|
|
29064
|
+
const errorMsg = error && error.message || error;
|
|
29065
|
+
console.error(`[entities-table-datasource] Service ${this._entityName} sent error: ${errorMsg}`, error);
|
|
29066
|
+
this.markAsLoaded();
|
|
29067
|
+
throw new Error(message || errorMsg);
|
|
29068
|
+
}
|
|
29069
|
+
handleServiceError(error) {
|
|
29070
|
+
const errorMsg = error && error.message || error;
|
|
29071
|
+
console.error(`[entities-table-datasource] Service ${this._entityName} sent error: ${errorMsg}`, error);
|
|
29072
|
+
this.markAsLoaded();
|
|
29073
|
+
throw error;
|
|
29074
|
+
}
|
|
29075
|
+
async delete(id) {
|
|
29076
|
+
// If new row: not need to propagate to the dataService
|
|
29077
|
+
if (id === -1) {
|
|
29078
|
+
return super.delete(id);
|
|
29079
|
+
}
|
|
29080
|
+
const row = this.getRow(id);
|
|
29081
|
+
if (!row) {
|
|
29082
|
+
console.error(`[entities-table-datasource] Row to delete with id=${id} not found`);
|
|
29083
|
+
return;
|
|
29084
|
+
}
|
|
29085
|
+
this.markAsLoading();
|
|
29086
|
+
try {
|
|
29087
|
+
await this.dataService.deleteAll([row.currentData], this.saveAllOptions);
|
|
29088
|
+
// Wait cache update, then table update
|
|
29089
|
+
await sleep(300);
|
|
29090
|
+
// make sure row has been deleted (because GraphQl cache remove can fail)
|
|
29091
|
+
const present = this.getRow(id) === row;
|
|
29092
|
+
if (present)
|
|
29093
|
+
await super.delete(id);
|
|
29094
|
+
this.markAsLoaded();
|
|
29095
|
+
}
|
|
29096
|
+
catch (err) {
|
|
29097
|
+
this.handleServiceError(err);
|
|
29098
|
+
}
|
|
29099
|
+
}
|
|
29100
|
+
async deleteAll(rows) {
|
|
29101
|
+
this.markAsLoading();
|
|
29102
|
+
const data = this.getDataFromRows(rows);
|
|
29103
|
+
try {
|
|
29104
|
+
// Call service deletion
|
|
29105
|
+
await this.dataService.deleteAll(data, this.saveAllOptions);
|
|
29106
|
+
// Workaround, to be sure all rows have been deleted
|
|
29107
|
+
// Sometime, the service miss deletion, or GraphQl cache remove failed.
|
|
29108
|
+
// In this case, apply missing deletion using the parent delete() function
|
|
29109
|
+
const rowNotDeleted = this.getRows().filter(row => rows.includes(row));
|
|
29110
|
+
const deleteFn = super.delete;
|
|
29111
|
+
if (isNotEmptyArray(rowNotDeleted)) {
|
|
29112
|
+
console.warn(`[entities-table-datasource] Force deletion of ${rowNotDeleted.length} rows! Please check that data service update the cache, after deletion`);
|
|
29113
|
+
rowNotDeleted
|
|
29114
|
+
// Start at the end
|
|
29115
|
+
.sort((a, b) => a.id > b.id ? -1 : 1)
|
|
29116
|
+
.forEach(r => deleteFn(r.id));
|
|
29117
|
+
}
|
|
29118
|
+
}
|
|
29119
|
+
catch (err) {
|
|
29120
|
+
// Handle service error
|
|
29121
|
+
this.handleServiceError(err);
|
|
29122
|
+
}
|
|
29123
|
+
finally {
|
|
29124
|
+
this.markAsLoaded();
|
|
29125
|
+
}
|
|
29126
|
+
}
|
|
29127
|
+
getRow(id) {
|
|
29128
|
+
return super.getRow(id);
|
|
29129
|
+
}
|
|
29130
|
+
getRows() {
|
|
29131
|
+
return this.rowsSubject.value || [];
|
|
29132
|
+
}
|
|
29133
|
+
getEditingRows() {
|
|
29134
|
+
return this.getRows().filter(row => row.editing);
|
|
29135
|
+
}
|
|
29136
|
+
getSingleEditingRow() {
|
|
29137
|
+
const rows = this.getRows();
|
|
29138
|
+
return rows.length === 1 ? rows[0] : undefined;
|
|
29139
|
+
}
|
|
29140
|
+
hasSomeEditingRow() {
|
|
29141
|
+
return this.getRows().some(row => row.editing);
|
|
29142
|
+
}
|
|
29143
|
+
async createNew(insertAt, opts = { editing: true }) {
|
|
29144
|
+
// Avoid multiple call (only one editing row is allowed)
|
|
29145
|
+
if (this._creating && opts.editing)
|
|
29146
|
+
return;
|
|
29147
|
+
this._creating = true;
|
|
29148
|
+
try {
|
|
29149
|
+
const row = await super.createNew(insertAt, opts);
|
|
29150
|
+
if (!row)
|
|
29151
|
+
return undefined; // Stop here
|
|
29152
|
+
// Call observers
|
|
29153
|
+
if (this.config?.onRowCreated) {
|
|
29154
|
+
try {
|
|
29155
|
+
await this.config.onRowCreated(row);
|
|
29156
|
+
}
|
|
29157
|
+
catch (err) {
|
|
29158
|
+
// Log, then continue
|
|
29159
|
+
console.error(err && err.message || err, err);
|
|
29160
|
+
}
|
|
29161
|
+
}
|
|
29162
|
+
return row;
|
|
29163
|
+
}
|
|
29164
|
+
finally {
|
|
29165
|
+
this._creating = false;
|
|
29166
|
+
}
|
|
29167
|
+
}
|
|
29168
|
+
getData() {
|
|
29169
|
+
const rows = this.getRows();
|
|
29170
|
+
return this.getDataFromRows(rows);
|
|
29171
|
+
}
|
|
29172
|
+
async fetchMore(opts) {
|
|
29173
|
+
if (!this._fetchMoreFn)
|
|
29174
|
+
return false; // Avoid multiple call
|
|
29175
|
+
if (this.hasSomeEditingRow()) {
|
|
29176
|
+
console.warn(`Cannot fetch more ${this._entityName} because some row) still editing`);
|
|
29177
|
+
return;
|
|
29178
|
+
}
|
|
29179
|
+
console.debug(`Will fetching more row(s) still...`);
|
|
29180
|
+
// Forget the fetchMore function, to avoid multiple call
|
|
29181
|
+
const fetchMoreFn = this._fetchMoreFn;
|
|
29182
|
+
this._fetchMoreFn = null;
|
|
29183
|
+
// Fetch next page
|
|
29184
|
+
const res = await fetchMoreFn();
|
|
29185
|
+
// Skip if empty (no more data)
|
|
29186
|
+
if (isEmptyArray(res?.data))
|
|
29187
|
+
return false;
|
|
29188
|
+
// Update the data source
|
|
29189
|
+
super.updateDatasource((this.currentData || []).concat(...res.data), opts);
|
|
29190
|
+
// Remember fetchMore
|
|
29191
|
+
this._fetchMoreFn = res.fetchMore;
|
|
29192
|
+
return true;
|
|
29193
|
+
}
|
|
29194
|
+
/* -- protected method -- */
|
|
29195
|
+
markAsLoading() {
|
|
29196
|
+
if (this.loadingSubject.value !== true) {
|
|
29197
|
+
this.loadingSubject.next(true);
|
|
29198
|
+
}
|
|
29199
|
+
}
|
|
29200
|
+
markAsLoaded() {
|
|
29201
|
+
if (this.loadingSubject.value !== false) {
|
|
29202
|
+
this.loadingSubject.next(false);
|
|
29203
|
+
}
|
|
29204
|
+
}
|
|
29205
|
+
}
|
|
29206
|
+
EntitiesAsyncTableDataSource.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EntitiesAsyncTableDataSource, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
29207
|
+
EntitiesAsyncTableDataSource.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: EntitiesAsyncTableDataSource, usesInheritance: true, ngImport: i0 });
|
|
29208
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EntitiesAsyncTableDataSource, decorators: [{
|
|
29209
|
+
type: Directive
|
|
29210
|
+
}], ctorParameters: function () { return [{ type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }]; } });
|
|
29211
|
+
|
|
29212
|
+
// @dynamic
|
|
29213
|
+
// noinspection DuplicatedCode
|
|
29214
|
+
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
29215
|
+
class AppAsyncTable {
|
|
29216
|
+
constructor(injector, columns, _dataSource, _filter) {
|
|
29217
|
+
this.columns = columns;
|
|
29218
|
+
this._dataSource = _dataSource;
|
|
29219
|
+
this._filter = _filter;
|
|
29220
|
+
this._initialized = false;
|
|
29221
|
+
this._subscription = new Subscription();
|
|
29222
|
+
this._cellValueChangesDefs = {};
|
|
29223
|
+
this._enabled = true;
|
|
29224
|
+
this.allowRowDetail = true;
|
|
29225
|
+
this.destroySubject = new Subject();
|
|
29226
|
+
this.excludesColumns = [];
|
|
29227
|
+
this.totalRowCount = null;
|
|
29228
|
+
this.readySubject = new BehaviorSubject(false);
|
|
29229
|
+
this.loadingSubject = new BehaviorSubject(true);
|
|
29230
|
+
this.savingSubject = new BehaviorSubject(false);
|
|
29231
|
+
this.touchedSubject = new BehaviorSubject(false);
|
|
29232
|
+
this.dirtySubject = new BehaviorSubject(false);
|
|
29233
|
+
this.errorSubject = new BehaviorSubject(undefined);
|
|
29234
|
+
this.selection = new SelectionModel(true, []);
|
|
29235
|
+
this.i18nColumnPrefix = 'COMMON.';
|
|
29236
|
+
this.autoLoad = true;
|
|
29237
|
+
this.focusFirstColumn = false;
|
|
29238
|
+
this.confirmBeforeDelete = false;
|
|
29239
|
+
this.confirmBeforeCancel = false;
|
|
29240
|
+
this.undoableDeletion = false;
|
|
29241
|
+
this.propagateRowError = false;
|
|
29242
|
+
this.defaultPageSize = DEFAULT_PAGE_SIZE;
|
|
29243
|
+
this.defaultPageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS;
|
|
29244
|
+
this.onRefresh = new EventEmitter();
|
|
29245
|
+
this.onOpenRow = new EventEmitter();
|
|
29246
|
+
this.onNewRow = new EventEmitter();
|
|
29247
|
+
this.onStartEditingRow = new EventEmitter();
|
|
29248
|
+
this.onConfirmEditCreateRow = new EventEmitter();
|
|
29249
|
+
this.onCancelOrDeleteRow = new EventEmitter();
|
|
29250
|
+
this.onBeforeDeleteRows = createPromiseEventEmitter();
|
|
29251
|
+
this.onBeforeCancelRows = createPromiseEventEmitter();
|
|
29252
|
+
this.onBeforeSave = createPromiseEventEmitter();
|
|
29253
|
+
this.onAfterDeletedRows = new EventEmitter();
|
|
29254
|
+
this.onSort = new EventEmitter();
|
|
29255
|
+
this.onDirty = new EventEmitter();
|
|
29256
|
+
this.onError = new EventEmitter();
|
|
29257
|
+
this._paginator = null;
|
|
29258
|
+
this.route = injector.get(ActivatedRoute);
|
|
29259
|
+
this.router = injector.get(Router);
|
|
29260
|
+
this.location = injector.get(Location);
|
|
29261
|
+
this.settings = injector.get(LocalSettingsService);
|
|
29262
|
+
this.translate = injector.get(TranslateService);
|
|
29263
|
+
this.modalCtrl = injector.get(ModalController);
|
|
29264
|
+
this.alertCtrl = injector.get(AlertController);
|
|
29265
|
+
this.toastController = injector.get(ToastController);
|
|
29266
|
+
this.formErrorAdapter = injector.get(FormErrorTranslator);
|
|
29267
|
+
this.mobile = this.settings.mobile;
|
|
29268
|
+
// Autocomplete fields
|
|
29269
|
+
this._autocompleteConfigHolder = new MatAutocompleteConfigHolder({
|
|
29270
|
+
getUserAttributes: (a, b) => this.settings.getFieldDisplayAttributes(a, b)
|
|
29271
|
+
});
|
|
29272
|
+
this.autocompleteFields = this._autocompleteConfigHolder.fields;
|
|
29273
|
+
}
|
|
29274
|
+
get error() {
|
|
29275
|
+
return this.errorSubject.value;
|
|
29276
|
+
}
|
|
29277
|
+
set error(error) {
|
|
29278
|
+
this.setError(error);
|
|
29279
|
+
}
|
|
29280
|
+
get firstUserColumn() {
|
|
29281
|
+
return this.displayedColumns[RESERVED_START_COLUMNS.length];
|
|
29282
|
+
}
|
|
29283
|
+
get lastUserColumn() {
|
|
29284
|
+
return this.displayedColumns[this.displayedColumns.length - RESERVED_END_COLUMNS.length - 1];
|
|
29285
|
+
}
|
|
29286
|
+
set dataSource(value) {
|
|
29287
|
+
this.setDatasource(value);
|
|
29288
|
+
}
|
|
29289
|
+
get dataSource() {
|
|
29290
|
+
return this._dataSource;
|
|
29291
|
+
}
|
|
29292
|
+
set filter(value) {
|
|
29293
|
+
this.setFilter(value);
|
|
29294
|
+
}
|
|
29295
|
+
get filter() {
|
|
29296
|
+
return this._filter;
|
|
29297
|
+
}
|
|
29298
|
+
get empty() {
|
|
29299
|
+
return this.loading || this.totalRowCount === 0;
|
|
29300
|
+
}
|
|
29301
|
+
get dirty() {
|
|
29302
|
+
return this.dirtySubject.value;
|
|
29303
|
+
}
|
|
29304
|
+
get valid() {
|
|
29305
|
+
return this.dataSource.getRows().every(row => row.editing ? row.valid : true);
|
|
29306
|
+
}
|
|
29307
|
+
get invalid() {
|
|
29308
|
+
return this.dataSource.getRows().some(row => row.editing ? row.invalid : false);
|
|
29309
|
+
}
|
|
29310
|
+
get pending() {
|
|
29311
|
+
return this.dataSource.getRows().some(row => row.editing ? row.pending : false);
|
|
29312
|
+
}
|
|
29313
|
+
get touched() {
|
|
29314
|
+
return this.touchedSubject.value;
|
|
29315
|
+
}
|
|
29316
|
+
get untouched() {
|
|
29317
|
+
return !this.touchedSubject.value;
|
|
29318
|
+
}
|
|
29319
|
+
disable(opts) {
|
|
29320
|
+
if (this.sort)
|
|
29321
|
+
this.sort.disabled = true;
|
|
29322
|
+
this._enabled = false;
|
|
29323
|
+
if (!opts || opts.emitEvent != false)
|
|
29324
|
+
this.markForCheck();
|
|
29325
|
+
}
|
|
29326
|
+
enable(opts) {
|
|
29327
|
+
if (this.sort)
|
|
29328
|
+
this.sort.disabled = false;
|
|
29329
|
+
this._enabled = true;
|
|
29330
|
+
if (!opts || opts.emitEvent != false)
|
|
29331
|
+
this.markForCheck();
|
|
29332
|
+
}
|
|
29333
|
+
get enabled() {
|
|
29334
|
+
return this._enabled;
|
|
29335
|
+
}
|
|
29336
|
+
// FIXME: need to hidden buttons (in HTML), etc. when disabled
|
|
29337
|
+
set disabled(value) {
|
|
29338
|
+
if (value !== !this._enabled) {
|
|
29339
|
+
if (value)
|
|
29340
|
+
this.disable({ emitEvent: false });
|
|
29341
|
+
else
|
|
29342
|
+
this.enable({ emitEvent: false });
|
|
29343
|
+
}
|
|
29344
|
+
}
|
|
29345
|
+
get disabled() {
|
|
29346
|
+
return !this._enabled;
|
|
29347
|
+
}
|
|
29348
|
+
markAsDirty(opts) {
|
|
29349
|
+
if (this.dirtySubject.value !== true) {
|
|
29350
|
+
this.dirtySubject.next(true);
|
|
29351
|
+
if (!opts || opts.emitEvent !== false) {
|
|
29352
|
+
this.markForCheck();
|
|
29353
|
+
}
|
|
29354
|
+
}
|
|
29355
|
+
}
|
|
29356
|
+
markAsPristine(opts) {
|
|
29357
|
+
if (this.dirtySubject.value !== false) {
|
|
29358
|
+
this.dirtySubject.next(false);
|
|
29359
|
+
if (!opts || opts.emitEvent !== false) {
|
|
29360
|
+
this.markForCheck();
|
|
29361
|
+
}
|
|
29362
|
+
}
|
|
29363
|
+
}
|
|
29364
|
+
async markAsUntouched(opts) {
|
|
29365
|
+
let needEmitEvent = false;
|
|
29366
|
+
if (this.touchedSubject.value) {
|
|
29367
|
+
this.touchedSubject.next(false);
|
|
29368
|
+
needEmitEvent = true;
|
|
29369
|
+
}
|
|
29370
|
+
if (this.dirty || this.dataSource.hasSomeEditingRow()) {
|
|
29371
|
+
for (const row of this.dataSource.getEditingRows()) {
|
|
29372
|
+
// Cancel the current editing row only if editing and if it was not previously saved
|
|
29373
|
+
await this.dataSource.cancelOrDelete(row);
|
|
29374
|
+
// Mark row as pristine
|
|
29375
|
+
await this.checkIfRowPristine(row, { emitEvent: false });
|
|
29376
|
+
}
|
|
29377
|
+
needEmitEvent = true;
|
|
29378
|
+
}
|
|
29379
|
+
this.previouslyEditedRowId = undefined;
|
|
29380
|
+
if (needEmitEvent && (!opts || opts.emitEvent !== false))
|
|
29381
|
+
this.markForCheck();
|
|
29382
|
+
}
|
|
29383
|
+
/**
|
|
29384
|
+
* @deprecated prefer to use markAllAsTouched()
|
|
29385
|
+
* @param opts
|
|
29386
|
+
*/
|
|
29387
|
+
markAsTouched(opts) {
|
|
29388
|
+
console.warn('TODO: Replace this call by markAllAsTouched() - because of changes in ngx-components >= 0.16.0');
|
|
29389
|
+
if (this.dataSource.hasSomeEditingRow()) {
|
|
29390
|
+
this.dataSource.getEditingRows().forEach(row => row.validator?.markAllAsTouched());
|
|
29391
|
+
if (!opts || opts.emitEvent !== false) {
|
|
29392
|
+
this.markForCheck();
|
|
29393
|
+
}
|
|
29394
|
+
}
|
|
29395
|
+
}
|
|
29396
|
+
markAllAsTouched(opts) {
|
|
29397
|
+
if (this.touchedSubject.value !== true) {
|
|
29398
|
+
this.touchedSubject.next(true);
|
|
29399
|
+
if (!opts || opts.emitEvent !== false)
|
|
29400
|
+
this.markForCheck();
|
|
29401
|
+
}
|
|
29402
|
+
if (this.dataSource.hasSomeEditingRow()) {
|
|
29403
|
+
this.dataSource.getEditingRows().forEach(row => row.validator?.markAllAsTouched());
|
|
29404
|
+
}
|
|
29405
|
+
}
|
|
29406
|
+
markAsSaving(opts) {
|
|
29407
|
+
if (this.savingSubject.value !== true) {
|
|
29408
|
+
this.focusColumn = undefined; // unselect focus column
|
|
29409
|
+
this.savingSubject.next(true);
|
|
29410
|
+
if (!opts || opts.emitEvent !== false)
|
|
29411
|
+
this.markForCheck();
|
|
29412
|
+
}
|
|
29413
|
+
}
|
|
29414
|
+
markAsSaved(opts) {
|
|
29415
|
+
if (this.savingSubject.value !== false) {
|
|
29416
|
+
this.savingSubject.next(false);
|
|
29417
|
+
if (!opts || opts.emitEvent !== false)
|
|
29418
|
+
this.markForCheck();
|
|
29419
|
+
}
|
|
29420
|
+
}
|
|
29421
|
+
markAsLoading(opts) {
|
|
29422
|
+
this.setLoading(true, opts);
|
|
29423
|
+
}
|
|
29424
|
+
markAsLoaded(opts) {
|
|
29425
|
+
this.setLoading(false, opts);
|
|
29426
|
+
}
|
|
29427
|
+
markAsReady(opts) {
|
|
29428
|
+
if (this.readySubject.value !== true) {
|
|
29429
|
+
this.readySubject.next(true);
|
|
29430
|
+
// If subclasses implements OnReady
|
|
29431
|
+
if (typeof this['ngOnReady'] === 'function') {
|
|
29432
|
+
this.ngOnReady();
|
|
29433
|
+
}
|
|
29434
|
+
}
|
|
29435
|
+
}
|
|
29436
|
+
get loading() {
|
|
29437
|
+
return this.loadingSubject.value;
|
|
29438
|
+
}
|
|
29439
|
+
get loaded() {
|
|
29440
|
+
return !this.loadingSubject.value;
|
|
29441
|
+
}
|
|
29442
|
+
enableSort() {
|
|
29443
|
+
if (this.sort)
|
|
29444
|
+
this.sort.disabled = false;
|
|
29445
|
+
}
|
|
29446
|
+
disableSort() {
|
|
29447
|
+
if (this.sort)
|
|
29448
|
+
this.sort.disabled = true;
|
|
29449
|
+
}
|
|
29450
|
+
set pageSize(value) {
|
|
29451
|
+
this.defaultPageSize = value;
|
|
29452
|
+
if (this.paginator) {
|
|
29453
|
+
this.paginator.pageSize = value;
|
|
29454
|
+
}
|
|
29455
|
+
}
|
|
29456
|
+
get pageSize() {
|
|
29457
|
+
return this.paginator && this.paginator.pageSize || this.defaultPageSize || DEFAULT_PAGE_SIZE;
|
|
29458
|
+
}
|
|
29459
|
+
get pageOffset() {
|
|
29460
|
+
return this.paginator && this.paginator.pageIndex * this.paginator.pageSize || 0;
|
|
29461
|
+
}
|
|
29462
|
+
get sortActive() {
|
|
29463
|
+
return this.sort && this.sort.active;
|
|
29464
|
+
}
|
|
29465
|
+
get sortDirection() {
|
|
29466
|
+
return this.sort && this.sort.direction && (this.sort.direction === 'desc' ? 'desc' : 'asc') || undefined;
|
|
29467
|
+
}
|
|
29468
|
+
set paginator(value) {
|
|
29469
|
+
this._paginator = value;
|
|
29470
|
+
}
|
|
29471
|
+
get paginator() {
|
|
29472
|
+
return this._paginator || this.childPaginator;
|
|
29473
|
+
}
|
|
29474
|
+
get destroyed() {
|
|
29475
|
+
return this.destroySubject?.closed !== false;
|
|
29476
|
+
}
|
|
29477
|
+
ngOnInit() {
|
|
29478
|
+
if (this._initialized)
|
|
29479
|
+
return; // Init only once
|
|
29480
|
+
this._initialized = true;
|
|
29481
|
+
// Set defaults
|
|
29482
|
+
this.readOnly = toBoolean(this.readOnly, this.dataSource?.config.readOnly || false); // read/write by default
|
|
29483
|
+
this.inlineEdition = !this.readOnly && toBoolean(this.inlineEdition, false); // force to false when readonly
|
|
29484
|
+
this.saveBeforeDelete = toBoolean(this.saveBeforeDelete, !this.readOnly); // force to false when readonly
|
|
29485
|
+
this.saveBeforeSort = toBoolean(this.saveBeforeSort, !this.readOnly); // force to false when readonly
|
|
29486
|
+
this.saveBeforeFilter = toBoolean(this.saveBeforeFilter, !this.readOnly); // force to false when readonly
|
|
29487
|
+
this.keepEditedRowOnSave = toBoolean(this.keepEditedRowOnSave, this.inlineEdition);
|
|
29488
|
+
this.errorTranslatorOptions = this.errorTranslatorOptions || { separator: ', ', controlPathTranslator: this }; // Can be override in subclasses constructors
|
|
29489
|
+
// Check ask user confirmation is possible
|
|
29490
|
+
if (this.confirmBeforeDelete && !this.alertCtrl)
|
|
29491
|
+
throw Error('Missing \'alertCtrl\' or \'injector\' in component\'s constructor.');
|
|
29492
|
+
// Defined unique id for settings for the page
|
|
29493
|
+
this.settingsId = this.settingsId || this.generateTableId();
|
|
29494
|
+
this.displayedColumns = this.getDisplayColumns();
|
|
29495
|
+
// Load the sorted columns, from settings
|
|
29496
|
+
{
|
|
29497
|
+
const sortedColumn = this.getSortedColumn();
|
|
29498
|
+
this.defaultSortBy = sortedColumn.id;
|
|
29499
|
+
this.defaultSortDirection = sortedColumn.start;
|
|
29500
|
+
}
|
|
29501
|
+
this.defaultPageSize = this.getPageSize();
|
|
29502
|
+
// Propagate error to event emitter
|
|
29503
|
+
this.registerSubscription(this.errorSubject
|
|
29504
|
+
.subscribe(value => this.onError.emit(value)));
|
|
29505
|
+
// Propagate dirty to event emitter
|
|
29506
|
+
this.registerSubscription(this.dirtySubject
|
|
29507
|
+
.subscribe(value => this.onDirty.emit(value)));
|
|
29508
|
+
// Propagate row dirty state to table
|
|
29509
|
+
this.registerSubscription(this.onStartEditingRow
|
|
29510
|
+
.pipe(filter(row => row?.validator && true), mergeMap(row => row.validator.valueChanges
|
|
29511
|
+
.pipe(filter(row => row.dirty), first(),
|
|
29512
|
+
// DEBUG
|
|
29513
|
+
//tap(() => console.debug("Propagate row's dirty to table..."))
|
|
29514
|
+
// Stop if next another row, or destroying
|
|
29515
|
+
takeUntil(this.onStartEditingRow), takeUntil(this.destroySubject))))
|
|
29516
|
+
.subscribe(() => this.markAsDirty()));
|
|
29517
|
+
// Call datasource refresh, on each refresh events
|
|
29518
|
+
this.registerSubscription(this.onRefresh
|
|
29519
|
+
.pipe(startWith((this.autoLoad ? {} : 'skip')), switchMap((event) => {
|
|
29520
|
+
this.dirtySubject.next(false);
|
|
29521
|
+
this.selection.clear();
|
|
29522
|
+
if (event === 'skip') {
|
|
29523
|
+
return of(undefined);
|
|
29524
|
+
}
|
|
29525
|
+
if (!this._dataSource) {
|
|
29526
|
+
if (this.debug)
|
|
29527
|
+
console.debug('[table] Skipping data load: no dataSource defined');
|
|
29528
|
+
return of(undefined);
|
|
29529
|
+
}
|
|
29530
|
+
if (this.debug)
|
|
29531
|
+
console.debug('[table] Calling dataSource.watchAll()...');
|
|
29532
|
+
return this._dataSource.watchAll(this.pageOffset, this.pageSize, this.sortActive, this.sortDirection, this._filter);
|
|
29533
|
+
}), catchError(err => {
|
|
29534
|
+
if (this.debug)
|
|
29535
|
+
console.error(err);
|
|
29536
|
+
this.setError(err && err.message || err);
|
|
29537
|
+
return of(undefined); // Continue
|
|
29538
|
+
}))
|
|
29539
|
+
.subscribe(res => this.updateView(res)));
|
|
29540
|
+
// Listen dataSource loading events
|
|
29541
|
+
if (this._dataSource)
|
|
29542
|
+
this.listenDatasourceLoading(this._dataSource);
|
|
29543
|
+
}
|
|
29544
|
+
ngAfterViewInit() {
|
|
29545
|
+
// Detect when parent ngOnInit() not call
|
|
29546
|
+
if (this.debug && !this.displayedColumns)
|
|
29547
|
+
console.warn(`[table] Missing 'displayedColumns'. Did you call parent ngOnInit() in component ${this.constructor.name} ?`);
|
|
29548
|
+
// Start listening sort and paginator events
|
|
29549
|
+
// noinspection JSIgnoredPromiseFromCall
|
|
29550
|
+
this.listenSortAndPaginationEvents();
|
|
29551
|
+
}
|
|
29552
|
+
ngOnDestroy() {
|
|
29553
|
+
this._subscription.unsubscribe();
|
|
29554
|
+
// Unsubscribe column value changes
|
|
29555
|
+
Object.keys(this._cellValueChangesDefs).forEach(col => this.stopCellValueChanges(col, true));
|
|
29556
|
+
this._cellValueChangesDefs = {};
|
|
29557
|
+
this.readySubject.unsubscribe();
|
|
29558
|
+
this.loadingSubject.unsubscribe();
|
|
29559
|
+
this.savingSubject.unsubscribe();
|
|
29560
|
+
this.errorSubject.unsubscribe();
|
|
29561
|
+
this.dirtySubject.unsubscribe();
|
|
29562
|
+
this.onRefresh.unsubscribe();
|
|
29563
|
+
this.onOpenRow.unsubscribe();
|
|
29564
|
+
this.onNewRow.unsubscribe();
|
|
29565
|
+
this.onStartEditingRow.unsubscribe();
|
|
29566
|
+
this.onConfirmEditCreateRow.unsubscribe();
|
|
29567
|
+
this.onCancelOrDeleteRow.unsubscribe();
|
|
29568
|
+
this.onBeforeDeleteRows.unsubscribe();
|
|
29569
|
+
this.onBeforeCancelRows.unsubscribe();
|
|
29570
|
+
this.onBeforeSave.unsubscribe();
|
|
29571
|
+
this.onAfterDeletedRows.unsubscribe();
|
|
29572
|
+
this.onSort.unsubscribe();
|
|
29573
|
+
this.onDirty.unsubscribe();
|
|
29574
|
+
this.onError.unsubscribe();
|
|
29575
|
+
this.destroySubject.next();
|
|
29576
|
+
this.destroySubject.unsubscribe();
|
|
29577
|
+
}
|
|
29578
|
+
async updateView(res, opts) {
|
|
29579
|
+
if (!res)
|
|
29580
|
+
return; // Skip (e.g error)
|
|
29581
|
+
if (res && res.data) {
|
|
29582
|
+
this.visibleRowCount = res.data.length;
|
|
29583
|
+
this.totalRowCount = isNotNil(res.total) ? res.total : ((this.paginator && this.paginator.pageIndex * (this.paginator.pageSize || DEFAULT_PAGE_SIZE) || 0) + this.visibleRowCount);
|
|
29584
|
+
if (this.debug)
|
|
29585
|
+
console.debug(`[table] ${res.data.length} rows loaded`);
|
|
29586
|
+
}
|
|
29587
|
+
else {
|
|
29588
|
+
//if (this.debug) console.debug('[table] NO rows loaded');
|
|
29589
|
+
this.totalRowCount = 0;
|
|
29590
|
+
this.visibleRowCount = 0;
|
|
29591
|
+
}
|
|
29592
|
+
if (!opts || opts.emitEvent !== false) {
|
|
29593
|
+
await this.markAsUntouched({ emitEvent: false });
|
|
29594
|
+
this.markAsPristine({ emitEvent: false });
|
|
29595
|
+
this.markAsLoaded({ emitEvent: false });
|
|
29596
|
+
}
|
|
29597
|
+
this.markForCheck();
|
|
29598
|
+
}
|
|
29599
|
+
setDatasource(datasource) {
|
|
29600
|
+
if (this._dataSource)
|
|
29601
|
+
throw new Error('[table] dataSource already set !');
|
|
29602
|
+
if (datasource && this._dataSource !== datasource) {
|
|
29603
|
+
this._dataSource = datasource;
|
|
29604
|
+
if (this._initialized)
|
|
29605
|
+
this.listenDatasourceLoading(datasource);
|
|
29606
|
+
}
|
|
29607
|
+
}
|
|
29608
|
+
resetDataSource() {
|
|
29609
|
+
if (this._dataSourceLoadingSubscription) {
|
|
29610
|
+
this._dataSourceLoadingSubscription.unsubscribe();
|
|
29611
|
+
this._subscription.remove(this._dataSourceLoadingSubscription);
|
|
29612
|
+
}
|
|
29613
|
+
//this._dataSource?.close();
|
|
29614
|
+
this._dataSource = null;
|
|
29615
|
+
}
|
|
29616
|
+
addColumnDef(column) {
|
|
29617
|
+
this.table.addColumnDef(column);
|
|
29618
|
+
}
|
|
29619
|
+
removeColumnDef(column) {
|
|
29620
|
+
this.table.removeColumnDef(column);
|
|
29621
|
+
}
|
|
29622
|
+
setFilter(filter, opts) {
|
|
29623
|
+
opts = opts || { emitEvent: true };
|
|
29624
|
+
if (this.saveBeforeFilter) {
|
|
29625
|
+
// if a dirty table is to be saved before filter
|
|
29626
|
+
if (this.dirty) {
|
|
29627
|
+
// Save
|
|
29628
|
+
this.saveBeforeAction('filter').then(saved => {
|
|
29629
|
+
// Apply filter only if user didn't cancel the save or the save is ok
|
|
29630
|
+
if (saved) {
|
|
29631
|
+
this.applyFilter(filter, opts);
|
|
29632
|
+
}
|
|
29633
|
+
});
|
|
29634
|
+
}
|
|
29635
|
+
else {
|
|
29636
|
+
// apply filter on non-dirty table
|
|
29637
|
+
this.applyFilter(filter, opts);
|
|
29638
|
+
}
|
|
29639
|
+
}
|
|
29640
|
+
else {
|
|
29641
|
+
// apply filter directly
|
|
29642
|
+
this.applyFilter(filter, opts);
|
|
29643
|
+
}
|
|
29644
|
+
}
|
|
29645
|
+
async confirmAndAdd(event, row) {
|
|
29646
|
+
if (!await this.confirmEditCreate(event, row)) {
|
|
29647
|
+
return false;
|
|
29648
|
+
}
|
|
29649
|
+
// Add row
|
|
29650
|
+
return await this.addRow(event);
|
|
29651
|
+
}
|
|
29652
|
+
async confirmAndBackward(event, row) {
|
|
29653
|
+
// Deleting edited row, if empty and not dirty
|
|
29654
|
+
if (this.dataSource.hasSomeEditingRow()) {
|
|
29655
|
+
for (const editingRow of this.dataSource.getEditingRows().filter(row => row.id === -1 && row.invalid && !row.dirty)) {
|
|
29656
|
+
await this.deleteNewRow(event, editingRow);
|
|
29657
|
+
}
|
|
29658
|
+
// Wait deletion is done, then edit previous row (by id, because of reloading)
|
|
29659
|
+
await this.waitIdle();
|
|
29660
|
+
await this.editRowById(event, row.id, { focusColumn: this.lastUserColumn });
|
|
29661
|
+
return true;
|
|
29662
|
+
}
|
|
29663
|
+
// Edit previous row
|
|
29664
|
+
await this.editRow(event, row, { focusColumn: this.lastUserColumn });
|
|
29665
|
+
return true;
|
|
29666
|
+
}
|
|
29667
|
+
async confirmAndForward(event, row) {
|
|
29668
|
+
if (!this.inlineEdition)
|
|
29669
|
+
return false;
|
|
29670
|
+
await this.confirmEditCreate(event, row);
|
|
29671
|
+
// Edit next row
|
|
29672
|
+
await this.editRowById(event, row.id + 1, { focusColumn: this.firstUserColumn });
|
|
29673
|
+
return true;
|
|
29674
|
+
}
|
|
29675
|
+
/**
|
|
29676
|
+
* Confirm the creation of the given row, or if not specified the currently edited row
|
|
29677
|
+
*
|
|
29678
|
+
* @param event
|
|
29679
|
+
* @param row
|
|
29680
|
+
*/
|
|
29681
|
+
async confirmEditCreate(event, row) {
|
|
29682
|
+
row = row || this.dataSource.getSingleEditingRow();
|
|
29683
|
+
if (!row || !row.editing)
|
|
29684
|
+
return true; // no row to confirm
|
|
29685
|
+
// Stop event
|
|
29686
|
+
event?.stopPropagation();
|
|
29687
|
+
// Confirmation edition or creation
|
|
29688
|
+
const confirmed = await row.confirmEditCreate();
|
|
29689
|
+
if (confirmed) {
|
|
29690
|
+
// Mark table as dirty (if row is dirty)
|
|
29691
|
+
if (row.dirty) {
|
|
29692
|
+
this.markAsDirty({ emitEvent: false /* because of resetError() */ });
|
|
29693
|
+
}
|
|
29694
|
+
// Clear error
|
|
29695
|
+
this.resetError();
|
|
29696
|
+
// Emit the confirm event
|
|
29697
|
+
this.onConfirmEditCreateRow.next(row);
|
|
29698
|
+
return true; // Continue
|
|
29699
|
+
}
|
|
29700
|
+
if (row.validator) {
|
|
29701
|
+
// If pending: Wait end of validation
|
|
29702
|
+
// TODO: remove when using async isValid() function
|
|
29703
|
+
if (row.pending) {
|
|
29704
|
+
await AppFormUtils.waitWhilePending(row.validator);
|
|
29705
|
+
}
|
|
29706
|
+
// NOT confirmed = row has error
|
|
29707
|
+
if (this.debug) {
|
|
29708
|
+
console.warn('[table] Cannot confirm row, because invalid');
|
|
29709
|
+
AppFormUtils.logFormErrors(row.validator, '[table] ');
|
|
29710
|
+
}
|
|
29711
|
+
// fix: mark all controls as touched to show errors
|
|
29712
|
+
row.validator.markAllAsTouched();
|
|
29713
|
+
// Compute row error, and propagate to table's error
|
|
29714
|
+
if (this.propagateRowError) {
|
|
29715
|
+
const error = this.getRowError(row);
|
|
29716
|
+
this.setError(error);
|
|
29717
|
+
}
|
|
29718
|
+
}
|
|
29719
|
+
// Not confirmed
|
|
29720
|
+
return false;
|
|
29721
|
+
}
|
|
29722
|
+
async cancelOrDelete(event, row, opts) {
|
|
29723
|
+
// Delete new row
|
|
29724
|
+
if (row.id === -1) {
|
|
29725
|
+
await this.deleteNewRow(event, row);
|
|
29726
|
+
}
|
|
29727
|
+
// Delete existing (but not editing) row
|
|
29728
|
+
else if (!row.editing) {
|
|
29729
|
+
await this.deleteExistingRow(event, row, opts);
|
|
29730
|
+
}
|
|
29731
|
+
// Cancel existing (and editing) row
|
|
29732
|
+
else {
|
|
29733
|
+
await this.cancelExistingRow(event, row, opts);
|
|
29734
|
+
}
|
|
29735
|
+
}
|
|
29736
|
+
async addRow(event, insertAt, opts) {
|
|
29737
|
+
if (this.debug)
|
|
29738
|
+
console.debug('[table] Asking for new row...');
|
|
29739
|
+
if (!this._enabled)
|
|
29740
|
+
return false;
|
|
29741
|
+
// Use modal if inline edition is disabled
|
|
29742
|
+
if (!this.inlineEdition) {
|
|
29743
|
+
await this.openNewRowDetail(event);
|
|
29744
|
+
return false;
|
|
29745
|
+
}
|
|
29746
|
+
// Try to finish edited row first
|
|
29747
|
+
if (!await this.confirmEditCreate()) {
|
|
29748
|
+
return false;
|
|
29749
|
+
}
|
|
29750
|
+
// Add new row
|
|
29751
|
+
const row = await this.addRowToTable(insertAt, opts);
|
|
29752
|
+
return !!row;
|
|
29753
|
+
}
|
|
29754
|
+
async save(opts) {
|
|
29755
|
+
opts = {
|
|
29756
|
+
keepEditing: this.keepEditedRowOnSave,
|
|
29757
|
+
...opts
|
|
29758
|
+
};
|
|
29759
|
+
if (this.readOnly) {
|
|
29760
|
+
throw { code: ErrorCodes.TABLE_READ_ONLY, message: 'ERROR.TABLE_READ_ONLY' };
|
|
29761
|
+
}
|
|
29762
|
+
this.resetError();
|
|
29763
|
+
// Keep edited row id (should be done BEFORE confirmEditCreate() )
|
|
29764
|
+
const editedRow = this.dataSource.getSingleEditingRow();
|
|
29765
|
+
this.previouslyEditedRowId = opts.keepEditing ? ((editedRow?.editing ? editedRow.id : undefined) || this.singleSelectedRow?.id) : undefined;
|
|
29766
|
+
const previouslyEditedData = opts.keepEditing ? (isNotNil(this.previouslyEditedRowId) && editedRow?.currentData || this.singleSelectedRow?.currentData) : undefined;
|
|
29767
|
+
if (!await this.confirmEditCreate()) {
|
|
29768
|
+
throw { code: ErrorCodes.TABLE_INVALID_ROW_ERROR, message: 'ERROR.TABLE_INVALID_ROW_ERROR' };
|
|
29769
|
+
}
|
|
29770
|
+
// Mark as saving
|
|
29771
|
+
this.markAsSaving();
|
|
29772
|
+
try {
|
|
29773
|
+
// Calling service save()
|
|
29774
|
+
if (this.debug)
|
|
29775
|
+
console.debug('[table] Calling dataSource.save()...');
|
|
29776
|
+
const isOK = await this._dataSource.save();
|
|
29777
|
+
if (isOK)
|
|
29778
|
+
this.markAsPristine();
|
|
29779
|
+
return isOK;
|
|
29780
|
+
}
|
|
29781
|
+
catch (err) {
|
|
29782
|
+
this.setError(err && err.message || err);
|
|
29783
|
+
throw err;
|
|
29784
|
+
}
|
|
29785
|
+
finally {
|
|
29786
|
+
this.markAsSaved();
|
|
29787
|
+
// Restore previous row
|
|
29788
|
+
if (isNotNil(this.previouslyEditedRowId)) {
|
|
29789
|
+
await this.selectRowByIdOrData(this.previouslyEditedRowId, previouslyEditedData);
|
|
29790
|
+
}
|
|
29791
|
+
}
|
|
29792
|
+
}
|
|
29793
|
+
async cancel(event, opts) {
|
|
29794
|
+
// Check confirmation
|
|
29795
|
+
if ((!opts || opts.interactive !== false) && this.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
|
|
29796
|
+
event?.stopPropagation();
|
|
29797
|
+
if (!await this.canCancelRows()) {
|
|
29798
|
+
return;
|
|
29799
|
+
}
|
|
29800
|
+
}
|
|
29801
|
+
this.onRefresh.emit();
|
|
29802
|
+
}
|
|
29803
|
+
async duplicateRow(event, row, opts) {
|
|
29804
|
+
event?.stopPropagation();
|
|
29805
|
+
row = row || this.singleSelectedRow;
|
|
29806
|
+
if (!row || !await this.confirmEditCreate(event, row)) {
|
|
29807
|
+
return false;
|
|
29808
|
+
}
|
|
29809
|
+
const newRow = await this.addRowToTable(row.id + 1);
|
|
29810
|
+
if (!newRow)
|
|
29811
|
+
throw new Error('Cannot add new row to table');
|
|
29812
|
+
const json = { ...row.currentData, id: null };
|
|
29813
|
+
// Reset some properties (e.g. rankOrder, etc)
|
|
29814
|
+
if (opts && opts.skipProperties) {
|
|
29815
|
+
const newData = newRow.currentData;
|
|
29816
|
+
opts.skipProperties.forEach(key => json[key] = newData[key]);
|
|
29817
|
+
}
|
|
29818
|
+
if (newRow.validator) {
|
|
29819
|
+
newRow.validator.patchValue(json);
|
|
29820
|
+
newRow.validator.markAsDirty();
|
|
29821
|
+
}
|
|
29822
|
+
else {
|
|
29823
|
+
if (newRow.currentData?.fromObject) {
|
|
29824
|
+
newRow.currentData.fromObject(json);
|
|
29825
|
+
}
|
|
29826
|
+
else {
|
|
29827
|
+
newRow.currentData = json;
|
|
29828
|
+
}
|
|
29829
|
+
this.markAsDirty();
|
|
29830
|
+
}
|
|
29831
|
+
// select
|
|
29832
|
+
await this.clickRow(undefined, newRow);
|
|
29833
|
+
}
|
|
29834
|
+
/** Whether the number of selected elements matches the total number of rows. */
|
|
29835
|
+
isAllSelected() {
|
|
29836
|
+
// DEBUG
|
|
29837
|
+
//console.debug('isAllSelected. lengths', this.selection.selected.length, this.totalRowCount);
|
|
29838
|
+
return this.selection.selected.length === this.totalRowCount ||
|
|
29839
|
+
this.selection.selected.length === this.visibleRowCount;
|
|
29840
|
+
}
|
|
29841
|
+
/** Selects all rows if they are not all selected; otherwise clear selection. */
|
|
29842
|
+
async masterToggle() {
|
|
29843
|
+
if (this.loading)
|
|
29844
|
+
return;
|
|
29845
|
+
if (this.isAllSelected()) {
|
|
29846
|
+
this.selection.clear();
|
|
29847
|
+
}
|
|
29848
|
+
else {
|
|
29849
|
+
const rows = this._dataSource.getRows();
|
|
29850
|
+
rows.forEach(row => this.selection.select(row));
|
|
29851
|
+
}
|
|
29852
|
+
}
|
|
29853
|
+
deleteSelection(event, opts) {
|
|
29854
|
+
return this.deleteRows(event, this.selection.selected, opts);
|
|
29855
|
+
}
|
|
29856
|
+
/**
|
|
29857
|
+
*
|
|
29858
|
+
* @param event
|
|
29859
|
+
* @param row
|
|
29860
|
+
* @param opts Use interactive=false to avoid user interaction (e.g. user confirmation)
|
|
29861
|
+
* And to force deletion even if table is busy
|
|
29862
|
+
*/
|
|
29863
|
+
async deleteRow(event, row, opts) {
|
|
29864
|
+
const deleteCount = await this.deleteRows(event, [row], opts);
|
|
29865
|
+
return deleteCount === 1;
|
|
29866
|
+
}
|
|
29867
|
+
/**
|
|
29868
|
+
*
|
|
29869
|
+
* @param event
|
|
29870
|
+
* @param rows
|
|
29871
|
+
* @param opts Use interactive=false to avoid user interaction (e.g. user confirmation)
|
|
29872
|
+
* And to force deletion even if table is busy
|
|
29873
|
+
*/
|
|
29874
|
+
async deleteRows(event, rows, opts) {
|
|
29875
|
+
if (this.readOnly) {
|
|
29876
|
+
throw { code: ErrorCodes.TABLE_READ_ONLY, message: 'ERROR.TABLE_READ_ONLY' };
|
|
29877
|
+
}
|
|
29878
|
+
if (event?.defaultPrevented)
|
|
29879
|
+
return 0; // SKip
|
|
29880
|
+
event?.preventDefault();
|
|
29881
|
+
if (!this._enabled || isEmptyArray(rows))
|
|
29882
|
+
return 0; // Skip is disabled, or no rows to delete
|
|
29883
|
+
if (this.loading && (!opts || opts.interactive !== false)) {
|
|
29884
|
+
console.warn('[app-table] Skip deleteRows() because table is busy (loading). Use opts.interactive = false to force deletion');
|
|
29885
|
+
return 0; // Skip if loading
|
|
29886
|
+
}
|
|
29887
|
+
// Make sure to keep newly created row
|
|
29888
|
+
const editedRow = this.dataSource.getSingleEditingRow();
|
|
29889
|
+
if (editedRow?.id === -1 && editedRow.editing && !rows.includes(editedRow)) {
|
|
29890
|
+
const confirmed = await this.confirmEditCreate();
|
|
29891
|
+
if (!confirmed)
|
|
29892
|
+
return 0; // Cannot delete (e.g. edited row is invalid)
|
|
29893
|
+
}
|
|
29894
|
+
// Check if it can delete
|
|
29895
|
+
const canDelete = await this.canDeleteRows(rows, opts);
|
|
29896
|
+
if (!canDelete)
|
|
29897
|
+
return 0; // Cannot delete
|
|
29898
|
+
// Reverse row order (on a copy)
|
|
29899
|
+
// This is a workaround, need because row.delete() has async execution
|
|
29900
|
+
// and index cache is updated with a delay
|
|
29901
|
+
let tempRows = rows.slice()
|
|
29902
|
+
.sort((a, b) => a.id > b.id ? -1 : 1);
|
|
29903
|
+
let deletedRows = [];
|
|
29904
|
+
// If data need to be saved first
|
|
29905
|
+
if (this.saveBeforeDelete) {
|
|
29906
|
+
// Exclude invalid rows (because of save() will fail, when exists some invalid rows)
|
|
29907
|
+
tempRows = tempRows.filter(row => {
|
|
29908
|
+
// Delete the row :
|
|
29909
|
+
// - if newly created row (id = -1),
|
|
29910
|
+
// - or if invalid and not editing (= not cancellable)
|
|
29911
|
+
if (row.id === -1 || (!row.editing && row.invalid /*do not use !valid because if row is disabled, it will be always !valid */)) {
|
|
29912
|
+
if (this.debug)
|
|
29913
|
+
console.debug(`[table] Delete row #${row.id}`);
|
|
29914
|
+
row.delete();
|
|
29915
|
+
this.visibleRowCount--;
|
|
29916
|
+
this.totalRowCount--;
|
|
29917
|
+
deletedRows.push(row);
|
|
29918
|
+
return false; // Exclude from the list to delete using the service
|
|
29919
|
+
}
|
|
29920
|
+
// Cancel the row (and mark as pristine), when not valid but in edition
|
|
29921
|
+
else if (row.editing && !row.valid) {
|
|
29922
|
+
row.cancel();
|
|
29923
|
+
// Mark row as pristine (if possible)
|
|
29924
|
+
this.checkIfRowPristine(row, { emitEvent: false, onlySelf: true /*avoid propagation to table*/ });
|
|
29925
|
+
return true; // Keep the row. the row will be deleted after the save
|
|
29926
|
+
}
|
|
29927
|
+
return true;
|
|
29928
|
+
});
|
|
29929
|
+
// Apply save, only if there is still some rows to delete
|
|
29930
|
+
// WARN: If no more rows (e.g. because all has been cancelled) then continue anyway to clear editedRow and selection (issue IMAGINE-669)
|
|
29931
|
+
if (deletedRows.length || tempRows.length) {
|
|
29932
|
+
// Save data (e.g. when using memory service)
|
|
29933
|
+
const saved = await this.saveBeforeAction('delete');
|
|
29934
|
+
if (!saved) {
|
|
29935
|
+
// Stop if save cancelled or save failed
|
|
29936
|
+
return;
|
|
29937
|
+
}
|
|
29938
|
+
}
|
|
29939
|
+
}
|
|
29940
|
+
try {
|
|
29941
|
+
// Apply deletion on datasource
|
|
29942
|
+
// If no more rows to delete, continue anyway to clear editedRow and selection (issue IMAGINE-669)
|
|
29943
|
+
if (tempRows.length) {
|
|
29944
|
+
if (this.debug)
|
|
29945
|
+
console.debug(`[table] Delete ${tempRows.length} rows...`);
|
|
29946
|
+
await this._dataSource.deleteAll(tempRows);
|
|
29947
|
+
}
|
|
29948
|
+
// DO not update manually, because watchALl().subscribe() will update this count
|
|
29949
|
+
//this.totalRowCount -= deleteCount;
|
|
29950
|
+
//this.visibleRowCount -= deleteCount;
|
|
29951
|
+
this.selection.clear();
|
|
29952
|
+
this.markAsDirty({ emitEvent: false /*markForCheck() is called just after*/ });
|
|
29953
|
+
this.markForCheck();
|
|
29954
|
+
this.onAfterDeletedRows.next(rows);
|
|
29955
|
+
return rows.length;
|
|
29956
|
+
}
|
|
29957
|
+
catch (err) {
|
|
29958
|
+
this.setError(err && err.message || err);
|
|
29959
|
+
throw err;
|
|
29960
|
+
}
|
|
29961
|
+
}
|
|
29962
|
+
async selectRowById(id) {
|
|
29963
|
+
if (id === undefined)
|
|
29964
|
+
return false;
|
|
29965
|
+
await this.waitIdle();
|
|
29966
|
+
const row = this.dataSource.getRow(id);
|
|
29967
|
+
if (!row)
|
|
29968
|
+
return false;
|
|
29969
|
+
return this.clickRow(null, row);
|
|
29970
|
+
}
|
|
29971
|
+
/**
|
|
29972
|
+
* Try to select row by data. Will use dataEquals() (that call EntityUtils.equals()) to find same row's data
|
|
29973
|
+
* @param data
|
|
29974
|
+
*/
|
|
29975
|
+
async selectRowByData(data) {
|
|
29976
|
+
if (data === undefined)
|
|
29977
|
+
return Promise.resolve(false);
|
|
29978
|
+
await this.waitIdle();
|
|
29979
|
+
const row = this.dataSource.getRows()
|
|
29980
|
+
.find(row => this.equals(row.currentData, data));
|
|
29981
|
+
if (!row)
|
|
29982
|
+
return false;
|
|
29983
|
+
return this.clickRow(null, row);
|
|
29984
|
+
}
|
|
29985
|
+
async clickRow(event, row) {
|
|
29986
|
+
if (this.loading) {
|
|
29987
|
+
// Wait while loading, and loop
|
|
29988
|
+
if (this.debug)
|
|
29989
|
+
console.debug('[table] Waiting before apply clickRow() (datasource is busy)...');
|
|
29990
|
+
await this.waitIdle({ timeout: 2000 });
|
|
29991
|
+
}
|
|
29992
|
+
// DEBUG
|
|
29993
|
+
//console.debug("[table] Detect click on row");
|
|
29994
|
+
if (row.id === -1 || row.editing)
|
|
29995
|
+
return true; // Already in edition
|
|
29996
|
+
if (event?.defaultPrevented)
|
|
29997
|
+
return false; // Cancelled by event
|
|
29998
|
+
// Open the detail page (if not inline editing)
|
|
29999
|
+
if (!this.inlineEdition) {
|
|
30000
|
+
if (event) {
|
|
30001
|
+
event.stopPropagation();
|
|
30002
|
+
event.preventDefault();
|
|
30003
|
+
}
|
|
30004
|
+
this.markAsLoading();
|
|
30005
|
+
this.selection.clear();
|
|
30006
|
+
this.openRow(row.currentData.id, row)
|
|
30007
|
+
.then(() => this.markAsLoaded())
|
|
30008
|
+
.catch(() => this.markAsLoaded());
|
|
30009
|
+
return true;
|
|
30010
|
+
}
|
|
30011
|
+
// Start editing row
|
|
30012
|
+
return this.editRow(event, row, { focusColumn: undefined /*force to use the click target*/ });
|
|
30013
|
+
}
|
|
30014
|
+
async moveRow(id, direction) {
|
|
30015
|
+
await this.dataSource.move(id, direction);
|
|
30016
|
+
}
|
|
30017
|
+
ready(opts) {
|
|
30018
|
+
return waitForTrue(this.readySubject, opts);
|
|
30019
|
+
}
|
|
30020
|
+
waitIdle(opts) {
|
|
30021
|
+
return waitForFalse(this.loadingSubject, opts);
|
|
30022
|
+
}
|
|
30023
|
+
async openSelectColumnsModal(event) {
|
|
30024
|
+
event?.preventDefault();
|
|
30025
|
+
// Copy current columns (deep copy)
|
|
30026
|
+
const columns = this.getCurrentColumns();
|
|
30027
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
30028
|
+
const modal = await this.modalCtrl.create({
|
|
30029
|
+
component: TableSelectColumnsComponent,
|
|
30030
|
+
componentProps: { columns },
|
|
30031
|
+
cssClass: hasTopModal && 'stack-modal'
|
|
30032
|
+
});
|
|
30033
|
+
// Open the modal
|
|
30034
|
+
await modal.present();
|
|
30035
|
+
// On dismiss
|
|
30036
|
+
const { data } = await modal.onDidDismiss();
|
|
30037
|
+
if (!data)
|
|
30038
|
+
return; // CANCELLED
|
|
30039
|
+
// Apply columns
|
|
30040
|
+
const userColumns = (data || []).filter(c => c.canHide === false || c.visible).map(c => c.name) || [];
|
|
30041
|
+
this.displayedColumns = RESERVED_START_COLUMNS.concat(userColumns).concat(RESERVED_END_COLUMNS);
|
|
30042
|
+
this.markForCheck();
|
|
30043
|
+
// Update user settings
|
|
30044
|
+
await this.settings.savePageSetting(this.settingsId, userColumns, SETTINGS_DISPLAY_COLUMNS);
|
|
30045
|
+
}
|
|
30046
|
+
trackByFn(index, row) {
|
|
30047
|
+
return row.id;
|
|
30048
|
+
}
|
|
30049
|
+
doRefresh(event) {
|
|
30050
|
+
this.onRefresh.emit(event);
|
|
30051
|
+
// When target wait for a complete (e.g. IonRefresher)
|
|
30052
|
+
if (event?.target && event.target.complete) {
|
|
30053
|
+
setTimeout(async () => {
|
|
30054
|
+
await this.waitIdle();
|
|
30055
|
+
event.target.complete();
|
|
30056
|
+
});
|
|
30057
|
+
}
|
|
30058
|
+
}
|
|
30059
|
+
getCurrentColumns() {
|
|
30060
|
+
const hiddenColumns = this.columns.slice(RESERVED_START_COLUMNS.length)
|
|
30061
|
+
.filter(name => this.displayedColumns.indexOf(name) === -1);
|
|
30062
|
+
return this.displayedColumns
|
|
30063
|
+
.concat(hiddenColumns)
|
|
30064
|
+
.filter(name => !RESERVED_START_COLUMNS.includes(name) && !RESERVED_END_COLUMNS.includes(name)
|
|
30065
|
+
&& !this.excludesColumns.includes(name))
|
|
30066
|
+
.map(name => ({
|
|
30067
|
+
name,
|
|
30068
|
+
label: this.getI18nColumnName(name),
|
|
30069
|
+
visible: this.displayedColumns.indexOf(name) !== -1,
|
|
30070
|
+
canHide: this.getRequiredColumns().indexOf(name) === -1
|
|
30071
|
+
}));
|
|
30072
|
+
}
|
|
30073
|
+
async escapeEditingRow(event, row) {
|
|
30074
|
+
row = row || this.dataSource.getSingleEditingRow();
|
|
30075
|
+
if (!row || !row.editing)
|
|
30076
|
+
return;
|
|
30077
|
+
// DEBUG
|
|
30078
|
+
//console.debug('[app-table] Cancel the row (keydown.escape)');
|
|
30079
|
+
if (event) {
|
|
30080
|
+
// Avoid to cancel the editor
|
|
30081
|
+
event.preventDefault();
|
|
30082
|
+
event.stopPropagation();
|
|
30083
|
+
}
|
|
30084
|
+
// If new row (no id)
|
|
30085
|
+
if (row.id === -1) {
|
|
30086
|
+
if (row.validator) {
|
|
30087
|
+
// If pending: Wait end of validation, then loop
|
|
30088
|
+
if (row.pending) {
|
|
30089
|
+
await AppFormUtils.waitWhilePending(row.validator);
|
|
30090
|
+
}
|
|
30091
|
+
// Row is invalid: delete the row
|
|
30092
|
+
if (row.invalid) {
|
|
30093
|
+
await this.deleteNewRow(event, row);
|
|
30094
|
+
return;
|
|
30095
|
+
}
|
|
30096
|
+
}
|
|
30097
|
+
}
|
|
30098
|
+
// If the row exists (has an id)
|
|
30099
|
+
else {
|
|
30100
|
+
// need to call cancel function (if confirmation will be called before)
|
|
30101
|
+
if (this.confirmBeforeCancel) {
|
|
30102
|
+
await this.cancelExistingRow(event, row, { keepEditing: false });
|
|
30103
|
+
return;
|
|
30104
|
+
}
|
|
30105
|
+
}
|
|
30106
|
+
// By default, try to confirm the row
|
|
30107
|
+
await this.confirmEditCreate(event, row);
|
|
30108
|
+
}
|
|
30109
|
+
translateControlPath(path) {
|
|
30110
|
+
// Can be overridden by subclasses, to resolve all field name
|
|
30111
|
+
// Use columns key, has default name
|
|
30112
|
+
const i18nColumnKey = this.getI18nColumnName(path);
|
|
30113
|
+
return this.translate?.instant(i18nColumnKey) || i18nColumnKey;
|
|
30114
|
+
}
|
|
30115
|
+
/* -- protected method -- */
|
|
30116
|
+
async editRow(event, row, opts) {
|
|
30117
|
+
if (!this._enabled || !this.inlineEdition)
|
|
30118
|
+
return false;
|
|
30119
|
+
if (this.dataSource.getEditingRows().includes(row))
|
|
30120
|
+
return true; // Already the edited row
|
|
30121
|
+
if (event?.defaultPrevented)
|
|
30122
|
+
return false;
|
|
30123
|
+
if (!await this.confirmEditCreate()) {
|
|
30124
|
+
return false;
|
|
30125
|
+
}
|
|
30126
|
+
if (!row.editing && !this.loading) {
|
|
30127
|
+
this.focusColumn = opts && opts.focusColumn || this.focusColumn;
|
|
30128
|
+
await this._dataSource.startEdit(row);
|
|
30129
|
+
}
|
|
30130
|
+
this.onStartEditingRow.emit(row);
|
|
30131
|
+
return true;
|
|
30132
|
+
}
|
|
30133
|
+
/**
|
|
30134
|
+
* Try to select row, by row.id, or by data. WIll use EntityUtils.equals()
|
|
30135
|
+
* @param id
|
|
30136
|
+
* @param data
|
|
30137
|
+
* @protected
|
|
30138
|
+
*/
|
|
30139
|
+
async selectRowByIdOrData(id, data) {
|
|
30140
|
+
let done = false;
|
|
30141
|
+
try {
|
|
30142
|
+
// Select by row id (if NOT a new row)
|
|
30143
|
+
if (isNotNil(id) || id !== -1) {
|
|
30144
|
+
done = await this.selectRowById(id);
|
|
30145
|
+
if (done)
|
|
30146
|
+
return true;
|
|
30147
|
+
console.warn('[app-table] Save: Cannot reselect row by row.id: ', id);
|
|
30148
|
+
}
|
|
30149
|
+
// Try by data
|
|
30150
|
+
if (data) {
|
|
30151
|
+
done = await this.selectRowByData(data);
|
|
30152
|
+
if (done)
|
|
30153
|
+
return true;
|
|
30154
|
+
console.warn('[app-table] Save: Cannot reselect row by data: ', data);
|
|
30155
|
+
}
|
|
30156
|
+
return false;
|
|
30157
|
+
}
|
|
30158
|
+
catch (err) {
|
|
30159
|
+
// Log, but continue
|
|
30160
|
+
console.error(err && err.message || err);
|
|
30161
|
+
return false;
|
|
30162
|
+
}
|
|
30163
|
+
}
|
|
30164
|
+
/**
|
|
30165
|
+
* return the selected row if unique in selection
|
|
30166
|
+
*/
|
|
30167
|
+
get singleSelectedRow() {
|
|
30168
|
+
return this.selection.selected?.length === 1 ? this.selection.selected[0] : undefined;
|
|
30169
|
+
}
|
|
30170
|
+
async canDeleteRows(rows, opts) {
|
|
30171
|
+
// Check using emitter
|
|
30172
|
+
if (this.onBeforeDeleteRows.observers.length > 0) {
|
|
30173
|
+
try {
|
|
30174
|
+
const canDelete = await emitPromiseEvent(this.onBeforeDeleteRows, 'canDelete', {
|
|
30175
|
+
detail: { rows }
|
|
30176
|
+
});
|
|
30177
|
+
if (!canDelete)
|
|
30178
|
+
return false;
|
|
30179
|
+
}
|
|
30180
|
+
catch (err) {
|
|
30181
|
+
if (err === 'CANCELLED')
|
|
30182
|
+
return false; // User cancel
|
|
30183
|
+
console.error('Error while checking if can delete rows', err);
|
|
30184
|
+
throw err;
|
|
30185
|
+
}
|
|
30186
|
+
}
|
|
30187
|
+
// Ask user confirmation
|
|
30188
|
+
if (this.confirmBeforeDelete && (!opts || opts.interactive !== false)) {
|
|
30189
|
+
return this.askDeleteConfirmation(null, rows);
|
|
30190
|
+
}
|
|
30191
|
+
return true;
|
|
30192
|
+
}
|
|
30193
|
+
async canCancelRows(rows, opts) {
|
|
30194
|
+
// Get dirty rows
|
|
30195
|
+
rows = rows || this.dataSource.getRows().filter(row => row.validator?.dirty);
|
|
30196
|
+
if (isEmptyArray(rows))
|
|
30197
|
+
return true; // No dirty: OK
|
|
30198
|
+
// Check using emitter
|
|
30199
|
+
if (this.onBeforeCancelRows.observers.length > 0) {
|
|
30200
|
+
try {
|
|
30201
|
+
const isCancel = await emitPromiseEvent(this.onBeforeCancelRows, 'canCancel', {
|
|
30202
|
+
detail: { rows }
|
|
30203
|
+
});
|
|
30204
|
+
if (!isCancel)
|
|
30205
|
+
return false;
|
|
30206
|
+
}
|
|
30207
|
+
catch (err) {
|
|
30208
|
+
if (err === 'CANCELLED')
|
|
30209
|
+
return false; // User cancel
|
|
30210
|
+
console.error('Error while checking if can cancel rows', err);
|
|
30211
|
+
throw err;
|
|
30212
|
+
}
|
|
30213
|
+
}
|
|
30214
|
+
// Ask user confirmation
|
|
30215
|
+
if (this.confirmBeforeCancel && (!opts || opts.interactive !== false)) {
|
|
30216
|
+
return this.askCancelConfirmation(null, rows);
|
|
30217
|
+
}
|
|
30218
|
+
return true;
|
|
30219
|
+
}
|
|
30220
|
+
async saveBeforeAction(saveAction) {
|
|
30221
|
+
if (!this.dirty) {
|
|
30222
|
+
// Continue without save
|
|
30223
|
+
return true;
|
|
30224
|
+
}
|
|
30225
|
+
let save;
|
|
30226
|
+
switch (saveAction) {
|
|
30227
|
+
case 'delete':
|
|
30228
|
+
save = this.saveBeforeDelete;
|
|
30229
|
+
break;
|
|
30230
|
+
case 'filter':
|
|
30231
|
+
save = this.saveBeforeFilter;
|
|
30232
|
+
break;
|
|
30233
|
+
case 'sort':
|
|
30234
|
+
save = this.saveBeforeSort;
|
|
30235
|
+
break;
|
|
30236
|
+
default:
|
|
30237
|
+
save = true;
|
|
30238
|
+
}
|
|
30239
|
+
// Default behavior
|
|
30240
|
+
let confirmed = true;
|
|
30241
|
+
if (save) {
|
|
30242
|
+
if (this.onBeforeSave.observers.length > 0) {
|
|
30243
|
+
// Ask confirmation
|
|
30244
|
+
try {
|
|
30245
|
+
const res = await emitPromiseEvent(this.onBeforeSave, 'beforeSave', {
|
|
30246
|
+
detail: { action: saveAction, valid: this.valid }
|
|
30247
|
+
});
|
|
30248
|
+
confirmed = res.confirmed;
|
|
30249
|
+
save = res.save;
|
|
30250
|
+
}
|
|
30251
|
+
catch (err) {
|
|
30252
|
+
if (err === 'CANCELLED')
|
|
30253
|
+
return false; // User cancel
|
|
30254
|
+
console.error('Error while checking if can delete rows', err);
|
|
30255
|
+
throw err;
|
|
30256
|
+
}
|
|
30257
|
+
}
|
|
30258
|
+
}
|
|
30259
|
+
if (confirmed) {
|
|
30260
|
+
if (save) {
|
|
30261
|
+
// User confirmed save
|
|
30262
|
+
const saved = await this.save();
|
|
30263
|
+
this.markAsDirty(); // Restore dirty flag
|
|
30264
|
+
return saved;
|
|
30265
|
+
}
|
|
30266
|
+
return true; // No save but continue action
|
|
30267
|
+
}
|
|
30268
|
+
return false; // User cancel the action
|
|
30269
|
+
}
|
|
30270
|
+
/**
|
|
30271
|
+
* Open a row detail view. By default, will to open row detail page.
|
|
30272
|
+
* Can be overridden by subclasses, BUT prefer to subscribe on onOpenRow
|
|
30273
|
+
* @param id
|
|
30274
|
+
* @param row
|
|
30275
|
+
* @protected
|
|
30276
|
+
*/
|
|
30277
|
+
async openRow(id, row) {
|
|
30278
|
+
if (this.allowRowDetail) {
|
|
30279
|
+
if (this.debug && this.dirty) {
|
|
30280
|
+
console.warn('[table] Opening row details, but table has unsaved changes!');
|
|
30281
|
+
}
|
|
30282
|
+
if (this.onOpenRow.observers.length) {
|
|
30283
|
+
this.onOpenRow.emit(row);
|
|
30284
|
+
return true;
|
|
30285
|
+
}
|
|
30286
|
+
// No ID defined: unable to open details
|
|
30287
|
+
if (isNil(id)) {
|
|
30288
|
+
console.warn('[table] Opening row details, but data has no id!');
|
|
30289
|
+
return false;
|
|
30290
|
+
}
|
|
30291
|
+
return this.router.navigate(['.', id.toString()], {
|
|
30292
|
+
relativeTo: this.route,
|
|
30293
|
+
queryParams: {}
|
|
30294
|
+
});
|
|
30295
|
+
}
|
|
30296
|
+
return false;
|
|
30297
|
+
}
|
|
30298
|
+
async openNewRowDetail(event) {
|
|
30299
|
+
if (!this.allowRowDetail)
|
|
30300
|
+
return false;
|
|
30301
|
+
if (this.onNewRow.observers.length > 0) {
|
|
30302
|
+
this.onNewRow.emit(event);
|
|
30303
|
+
return true;
|
|
30304
|
+
}
|
|
30305
|
+
return await this.router.navigate(['new'], {
|
|
30306
|
+
relativeTo: this.route
|
|
30307
|
+
});
|
|
30308
|
+
}
|
|
30309
|
+
// can be overridden to add more required columns
|
|
30310
|
+
getRequiredColumns() {
|
|
30311
|
+
return DEFAULT_REQUIRED_COLUMNS;
|
|
30312
|
+
}
|
|
30313
|
+
getUserColumns() {
|
|
30314
|
+
return this.settings.getPageSettings(this.settingsId, SETTINGS_DISPLAY_COLUMNS);
|
|
30315
|
+
}
|
|
30316
|
+
getSortedColumn() {
|
|
30317
|
+
const data = this.settings.getPageSettings(this.settingsId, SETTINGS_SORTED_COLUMN);
|
|
30318
|
+
const parts = data && data.split(':');
|
|
30319
|
+
if (parts && parts.length === 2 && this.columns.includes(parts[0])) {
|
|
30320
|
+
return { id: parts[0], start: parts[1] === 'desc' ? 'desc' : 'asc', disableClear: false };
|
|
30321
|
+
}
|
|
30322
|
+
if (this.defaultSortBy) {
|
|
30323
|
+
return { id: this.defaultSortBy, start: this.defaultSortDirection || 'asc', disableClear: false };
|
|
30324
|
+
}
|
|
30325
|
+
return { id: 'id', start: 'asc', disableClear: false };
|
|
30326
|
+
}
|
|
30327
|
+
getPageSize() {
|
|
30328
|
+
const pageSize = this.settings.getPageSettings(this.settingsId, SETTINGS_PAGE_SIZE);
|
|
30329
|
+
return pageSize || this.defaultPageSize;
|
|
30330
|
+
}
|
|
30331
|
+
getDisplayColumns() {
|
|
30332
|
+
let userColumns = this.getUserColumns();
|
|
30333
|
+
// No user override
|
|
30334
|
+
if (!userColumns) {
|
|
30335
|
+
// Return default, without columns to hide
|
|
30336
|
+
return this.columns.filter(column => !this.excludesColumns.includes(column));
|
|
30337
|
+
}
|
|
30338
|
+
// Get fixed start columns
|
|
30339
|
+
const fixedStartColumns = this.columns.filter(c => RESERVED_START_COLUMNS.includes(c));
|
|
30340
|
+
// Remove end columns
|
|
30341
|
+
const fixedEndColumns = this.columns.filter(c => RESERVED_END_COLUMNS.includes(c));
|
|
30342
|
+
// Remove fixed columns from user columns
|
|
30343
|
+
userColumns = userColumns.filter(c => !fixedStartColumns.includes(c) && !fixedEndColumns.includes(c) && this.columns.includes(c));
|
|
30344
|
+
// Add required columns if missing
|
|
30345
|
+
userColumns.push(...this.getRequiredColumns().filter(c => !fixedStartColumns.includes(c) && !fixedEndColumns.includes(c) && !userColumns.includes(c)));
|
|
30346
|
+
return fixedStartColumns
|
|
30347
|
+
.concat(userColumns)
|
|
30348
|
+
.concat(fixedEndColumns)
|
|
30349
|
+
// Remove columns to hide
|
|
30350
|
+
.filter(column => !this.excludesColumns.includes(column));
|
|
30351
|
+
}
|
|
30352
|
+
/**
|
|
30353
|
+
* Recompute display columns
|
|
30354
|
+
*
|
|
30355
|
+
* @protected
|
|
30356
|
+
*/
|
|
30357
|
+
updateColumns() {
|
|
30358
|
+
this.displayedColumns = this.getDisplayColumns();
|
|
30359
|
+
if (!this.loading)
|
|
30360
|
+
this.markForCheck();
|
|
30361
|
+
}
|
|
30362
|
+
registerSubscription(sub) {
|
|
30363
|
+
this._subscription.add(sub);
|
|
30364
|
+
}
|
|
30365
|
+
unregisterSubscription(sub) {
|
|
30366
|
+
this._subscription.remove(sub);
|
|
30367
|
+
}
|
|
30368
|
+
registerAutocompleteField(fieldName, options) {
|
|
30369
|
+
return this._autocompleteConfigHolder.add(fieldName, options);
|
|
30370
|
+
}
|
|
30371
|
+
getI18nColumnName(columnName) {
|
|
30372
|
+
return (this.i18nColumnPrefix || '') + changeCaseToUnderscore(columnName).toUpperCase();
|
|
30373
|
+
}
|
|
30374
|
+
generateTableId() {
|
|
30375
|
+
// noinspection JSNonASCIINames
|
|
30376
|
+
const id = this.location.path(true)
|
|
30377
|
+
.replace(/[?].*$/g, '')
|
|
30378
|
+
.replace(/\/\d+/g, '_id')
|
|
30379
|
+
+ '_'
|
|
30380
|
+
// Get a component unique name - See https://stackoverflow.com/questions/60114682/how-to-access-components-unique-encapsulation-id-in-angular-9
|
|
30381
|
+
+ (this.constructor['ɵcmp']?.id || this.constructor.name);
|
|
30382
|
+
//if (this.debug) console.debug("[table] id = " + id);
|
|
30383
|
+
return id;
|
|
30384
|
+
}
|
|
30385
|
+
async addRowToTable(insertAt, opts) {
|
|
30386
|
+
// Try to finish edited row first
|
|
30387
|
+
if (!await this.confirmEditCreate()) {
|
|
30388
|
+
console.warn('[table] Cannot add new row, because the previous edited row cannot be confirmed');
|
|
30389
|
+
return undefined;
|
|
30390
|
+
}
|
|
30391
|
+
const editing = this.inlineEdition && (!opts || opts.editing !== false); // true by default, if inlineEdition
|
|
30392
|
+
const row = await this._dataSource.createNew(insertAt, { editing });
|
|
30393
|
+
if (!row)
|
|
30394
|
+
return undefined;
|
|
30395
|
+
if (row.editing) {
|
|
30396
|
+
// Update focused column
|
|
30397
|
+
this.focusFirstColumn = true;
|
|
30398
|
+
this.focusColumn = opts?.focusColumn || this.firstUserColumn;
|
|
30399
|
+
// Emit start editing event
|
|
30400
|
+
this.onStartEditingRow.emit(row);
|
|
30401
|
+
}
|
|
30402
|
+
this.totalRowCount++;
|
|
30403
|
+
this.visibleRowCount++;
|
|
30404
|
+
this.markAsDirty({ emitEvent: false /*markForCheck() is called just after*/ });
|
|
30405
|
+
// Emit event
|
|
30406
|
+
if (!opts || opts.emitEvent !== false)
|
|
30407
|
+
this.markForCheck();
|
|
30408
|
+
return row;
|
|
30409
|
+
}
|
|
30410
|
+
registerCellValueChanges(name, formPath, emitInitialValue) {
|
|
30411
|
+
formPath = formPath || name;
|
|
30412
|
+
emitInitialValue = emitInitialValue || false;
|
|
30413
|
+
let def = this._cellValueChangesDefs[name];
|
|
30414
|
+
if (def && (def.formPath !== formPath || def.emitInitialValue !== emitInitialValue)) {
|
|
30415
|
+
throw Error('Already register a cell value change for this name, with different \'formPath\' or \'emitInitialValue\'. Please use same arguments.');
|
|
30416
|
+
}
|
|
30417
|
+
// Not exists: register new definition
|
|
30418
|
+
if (!def) {
|
|
30419
|
+
if (this.debug)
|
|
30420
|
+
console.debug(`[table] New listener {${name}} for value changes on path ${formPath}`);
|
|
30421
|
+
def = {
|
|
30422
|
+
subject: new Subject(),
|
|
30423
|
+
subscription: null,
|
|
30424
|
+
formPath,
|
|
30425
|
+
emitInitialValue
|
|
30426
|
+
};
|
|
30427
|
+
this._cellValueChangesDefs[name] = def;
|
|
30428
|
+
// Start the listener, when editing starts
|
|
30429
|
+
this.registerSubscription(this.onStartEditingRow.subscribe(row => this.startCellValueChanges(name, row)));
|
|
30430
|
+
}
|
|
30431
|
+
return def.subject;
|
|
30432
|
+
}
|
|
30433
|
+
setShowColumn(columnName, show, opts) {
|
|
30434
|
+
if (!this.excludesColumns.includes(columnName) !== show) {
|
|
30435
|
+
if (!show) {
|
|
30436
|
+
this.excludesColumns.push(columnName);
|
|
30437
|
+
}
|
|
30438
|
+
else {
|
|
30439
|
+
const index = this.excludesColumns.findIndex(value => value === columnName);
|
|
30440
|
+
if (index >= 0)
|
|
30441
|
+
this.excludesColumns.splice(index, 1);
|
|
30442
|
+
}
|
|
30443
|
+
// Recompute display columns
|
|
30444
|
+
if (this.displayedColumns && (!opts || opts.emitEvent !== false)) {
|
|
30445
|
+
this.updateColumns();
|
|
30446
|
+
}
|
|
30447
|
+
}
|
|
30448
|
+
}
|
|
30449
|
+
getShowColumn(columnName) {
|
|
30450
|
+
return !this.excludesColumns.includes(columnName);
|
|
30451
|
+
}
|
|
30452
|
+
startsWithUpperCase(input, search) {
|
|
30453
|
+
return input && input.toUpperCase().startsWith(search);
|
|
30454
|
+
}
|
|
30455
|
+
markForCheck() {
|
|
30456
|
+
// Should be overridden by subclasses, depending on ChangeDetectionStrategy
|
|
30457
|
+
}
|
|
30458
|
+
async askDeleteConfirmation(event, rows) {
|
|
30459
|
+
if (this.undoableDeletion) {
|
|
30460
|
+
// Special message, for undoable deletion
|
|
30461
|
+
return Alerts.askConfirmation(rows?.length === 1 ? 'CONFIRM.DELETE_ROW' : 'CONFIRM.DELETE_ROWS', this.alertCtrl, this.translate, event);
|
|
30462
|
+
}
|
|
30463
|
+
// Immediate deletion action
|
|
30464
|
+
return Alerts.askActionConfirmation(this.alertCtrl, this.translate, true, event);
|
|
30465
|
+
}
|
|
30466
|
+
async askCancelConfirmation(event, rows) {
|
|
30467
|
+
return Alerts.askConfirmation(rows?.length === 1 ? 'CONFIRM.CANCEL_ROW' : 'CONFIRM.CANCEL_ROWS', this.alertCtrl, this.translate, event);
|
|
30468
|
+
}
|
|
30469
|
+
async askRestoreConfirmation(event) {
|
|
30470
|
+
return Alerts.askActionConfirmation(this.alertCtrl, this.translate, false, event);
|
|
30471
|
+
}
|
|
30472
|
+
async showToast(opts) {
|
|
30473
|
+
if (!this.toastController)
|
|
30474
|
+
throw new Error('Missing toastController in component\'s constructor');
|
|
30475
|
+
return Toasts.show(this.toastController, this.translate, opts);
|
|
30476
|
+
}
|
|
30477
|
+
resetError(opts) {
|
|
30478
|
+
this.setError(undefined, opts);
|
|
30479
|
+
}
|
|
30480
|
+
getRowError(row, opts) {
|
|
30481
|
+
row = row || this.dataSource.getSingleEditingRow();
|
|
30482
|
+
if (!row || !this.formErrorAdapter)
|
|
30483
|
+
return undefined;
|
|
30484
|
+
return this.formErrorAdapter.translateFormErrors(row.validator, {
|
|
30485
|
+
...this.errorTranslatorOptions,
|
|
30486
|
+
...opts
|
|
30487
|
+
});
|
|
30488
|
+
}
|
|
30489
|
+
setError(value, opts) {
|
|
30490
|
+
if (this.errorSubject.value !== value) {
|
|
30491
|
+
this.errorSubject.next(value);
|
|
30492
|
+
if (!opts || opts.emitEvent !== false) {
|
|
30493
|
+
this.markForCheck();
|
|
30494
|
+
}
|
|
30495
|
+
}
|
|
30496
|
+
}
|
|
30497
|
+
/**
|
|
30498
|
+
* Compare data equality (default by id)
|
|
30499
|
+
* Can be overridden to add additional properties to compare
|
|
30500
|
+
*
|
|
30501
|
+
* @param d1
|
|
30502
|
+
* @param d2
|
|
30503
|
+
* @protected
|
|
30504
|
+
*/
|
|
30505
|
+
equals(d1, d2) {
|
|
30506
|
+
return EntityUtils.equals(d1, d2, 'id');
|
|
30507
|
+
}
|
|
30508
|
+
markRowAsDirty(row, opts) {
|
|
30509
|
+
row = row || this.dataSource.getSingleEditingRow();
|
|
30510
|
+
if (row)
|
|
30511
|
+
row.validator?.markAsDirty(opts);
|
|
30512
|
+
this.markAsDirty(opts);
|
|
30513
|
+
}
|
|
30514
|
+
/* -- private method -- */
|
|
30515
|
+
async listenSortAndPaginationEvents() {
|
|
30516
|
+
if (!this.table) {
|
|
30517
|
+
// DEBUG only -- alert user that table not found in template
|
|
30518
|
+
if (this.debug) {
|
|
30519
|
+
setTimeout(() => !this.table && console.warn(`[table] Missing <mat-table> in the HTML template (after waiting 500ms)! Component: ${this.constructor.name}`), 500);
|
|
30520
|
+
}
|
|
30521
|
+
// Make sure to wait the table
|
|
30522
|
+
await waitFor(() => !!this.table, { stop: this.destroySubject, stopError: false /*avoid error when destorying the table*/ });
|
|
30523
|
+
}
|
|
30524
|
+
this.registerSubscription(merge(
|
|
30525
|
+
// Listen sort events
|
|
30526
|
+
this.sort && this.sort.sortChange
|
|
30527
|
+
.pipe(filter(() => !this.sort.disabled), mergeMap(async () => this.saveBeforeAction('sort')), filter(res => res === true),
|
|
30528
|
+
// Save sort in settings
|
|
30529
|
+
tap(() => {
|
|
30530
|
+
const value = [this.sort.active, this.sort.direction || 'asc'].join(':');
|
|
30531
|
+
this.settings.savePageSetting(this.settingsId, value, SETTINGS_SORTED_COLUMN);
|
|
30532
|
+
}))
|
|
30533
|
+
|| EMPTY,
|
|
30534
|
+
// Listen paginator events
|
|
30535
|
+
this.paginator && this.paginator.page
|
|
30536
|
+
.pipe(mergeMap((_) => this.saveBeforeAction('sort')), filter(saved => saved === true),
|
|
30537
|
+
// Save page size in settings
|
|
30538
|
+
tap(() => this.settings.savePageSetting(this.settingsId, this.paginator.pageSize, SETTINGS_PAGE_SIZE))) || EMPTY)
|
|
30539
|
+
// Refresh on any sort or paginator events
|
|
30540
|
+
.subscribe(value => {
|
|
30541
|
+
this.onSort.emit(value);
|
|
30542
|
+
this.onRefresh.emit(value);
|
|
30543
|
+
}));
|
|
30544
|
+
// If the user changes the sort order, reset back to the first page.
|
|
30545
|
+
if (this.sort && this.paginator) {
|
|
30546
|
+
this.registerSubscription(this.sort.sortChange
|
|
30547
|
+
.pipe(filter(() => !this.sort.disabled))
|
|
30548
|
+
.subscribe(() => this.paginator.pageIndex = 0));
|
|
30549
|
+
}
|
|
30550
|
+
}
|
|
30551
|
+
async editRowById(event, id, opts) {
|
|
30552
|
+
if (id < 0)
|
|
30553
|
+
return;
|
|
30554
|
+
if (id >= this.visibleRowCount) {
|
|
30555
|
+
await this.addRow(event, undefined, { ...opts, editing: true });
|
|
30556
|
+
}
|
|
30557
|
+
else {
|
|
30558
|
+
const row = await this.dataSource.getRow(id);
|
|
30559
|
+
await this.editRow(event, row, opts);
|
|
30560
|
+
}
|
|
30561
|
+
}
|
|
30562
|
+
setLoading(value, opts) {
|
|
30563
|
+
if (this.loadingSubject.value !== value) {
|
|
30564
|
+
this.loadingSubject.next(value);
|
|
30565
|
+
if (!opts || opts.emitEvent !== false) {
|
|
30566
|
+
this.markForCheck();
|
|
30567
|
+
}
|
|
30568
|
+
}
|
|
30569
|
+
}
|
|
30570
|
+
async deleteNewRow(event, row) {
|
|
30571
|
+
if (row.id !== -1)
|
|
30572
|
+
throw new Error('Row must have id = -1');
|
|
30573
|
+
event?.stopPropagation();
|
|
30574
|
+
this.selection.clear();
|
|
30575
|
+
await this._dataSource.cancelOrDelete(row);
|
|
30576
|
+
this.onCancelOrDeleteRow.next(row);
|
|
30577
|
+
this.resetError();
|
|
30578
|
+
this.totalRowCount--;
|
|
30579
|
+
this.visibleRowCount--;
|
|
30580
|
+
}
|
|
30581
|
+
async deleteExistingRow(event, row, opts) {
|
|
30582
|
+
// Make sure row will be cancelled, and NOT deleted
|
|
30583
|
+
if (row.id === -1)
|
|
30584
|
+
throw new Error('Row must have an id');
|
|
30585
|
+
if (event?.defaultPrevented)
|
|
30586
|
+
return 0; // SKip
|
|
30587
|
+
event?.preventDefault();
|
|
30588
|
+
await this.deleteRow(null, row, opts);
|
|
30589
|
+
}
|
|
30590
|
+
async cancelExistingRow(event, row, opts) {
|
|
30591
|
+
// Make sure row will be cancelled, and NOT deleted
|
|
30592
|
+
if (row.id === -1 || !row.editing)
|
|
30593
|
+
throw new Error('Row cannot be canceling, but only deleting');
|
|
30594
|
+
const confirmed = (!opts || opts.interactive !== false);
|
|
30595
|
+
// Ask user confirmation, if cancel
|
|
30596
|
+
if (!confirmed && row.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observers.length > 0)) {
|
|
30597
|
+
event.stopPropagation();
|
|
30598
|
+
if (!await this.canCancelRows([row], opts)) {
|
|
30599
|
+
return;
|
|
30600
|
+
}
|
|
30601
|
+
}
|
|
30602
|
+
const keepEditing = row.editing && (!opts || opts.keepEditing !== false);
|
|
30603
|
+
await this._dataSource.cancelOrDelete(row);
|
|
30604
|
+
this.onCancelOrDeleteRow.next(row);
|
|
30605
|
+
// Mark row as pristine
|
|
30606
|
+
await this.checkIfRowPristine(row);
|
|
30607
|
+
// Restore editing state
|
|
30608
|
+
if (keepEditing) {
|
|
30609
|
+
await this.editRow(undefined, row);
|
|
30610
|
+
}
|
|
30611
|
+
}
|
|
30612
|
+
async checkIfRowPristine(row, opts) {
|
|
30613
|
+
// Mark row as pristine
|
|
30614
|
+
const markRowAsPristine = this.dataSource?.config.restoreOriginalDataOnCancel === true;
|
|
30615
|
+
if (markRowAsPristine) {
|
|
30616
|
+
row.validator?.markAsPristine();
|
|
30617
|
+
// Check if table is now pristine
|
|
30618
|
+
if (!opts || opts.onlySelf !== true) {
|
|
30619
|
+
await this.checkIfPristine(opts);
|
|
30620
|
+
}
|
|
30621
|
+
}
|
|
30622
|
+
}
|
|
30623
|
+
async checkIfPristine(opts) {
|
|
30624
|
+
if (!this.dirty)
|
|
30625
|
+
return; // Already pristine
|
|
30626
|
+
const rows = this._dataSource.getRows();
|
|
30627
|
+
const pristine = (rows || []).findIndex(row => row.dirty) === -1;
|
|
30628
|
+
if (pristine)
|
|
30629
|
+
this.markAsPristine(opts);
|
|
30630
|
+
}
|
|
30631
|
+
applyFilter(filter, opts) {
|
|
30632
|
+
if (this.debug)
|
|
30633
|
+
console.debug('[table] Applying filter', filter);
|
|
30634
|
+
this._filter = filter;
|
|
30635
|
+
if (opts && opts.emitEvent) {
|
|
30636
|
+
if (this.paginator && this.paginator.pageIndex > 0) {
|
|
30637
|
+
this.paginator.pageIndex = 0;
|
|
30638
|
+
}
|
|
30639
|
+
this.onRefresh.emit();
|
|
30640
|
+
}
|
|
30641
|
+
}
|
|
30642
|
+
listenDatasourceLoading(dataSource) {
|
|
30643
|
+
if (!dataSource)
|
|
30644
|
+
throw new Error('[table] dataSource not set !');
|
|
30645
|
+
// Cleaning previous subscription on datasource
|
|
30646
|
+
if (isNotNil(this._dataSourceLoadingSubscription)) {
|
|
30647
|
+
if (this.debug)
|
|
30648
|
+
console.debug('[table] Many call to listenDatasource(): Cleaning previous subscriptions...');
|
|
30649
|
+
this._dataSourceLoadingSubscription.unsubscribe();
|
|
30650
|
+
this.unregisterSubscription(this._dataSourceLoadingSubscription);
|
|
30651
|
+
}
|
|
30652
|
+
// Propage loading to table
|
|
30653
|
+
this._dataSourceLoadingSubscription = this._dataSource.loadingSubject
|
|
30654
|
+
.pipe(distinctUntilChanged(),
|
|
30655
|
+
// If changed to True: propagate as soon as possible
|
|
30656
|
+
tap((loading) => loading && this.setLoading(true)),
|
|
30657
|
+
// If changed to False: wait 250ms before propagate (to make sure the spinner has been displayed)
|
|
30658
|
+
debounceTime(250), tap(loading => !loading && this.setLoading(false)))
|
|
30659
|
+
.subscribe();
|
|
30660
|
+
this.registerSubscription(this._dataSourceLoadingSubscription);
|
|
30661
|
+
}
|
|
30662
|
+
startCellValueChanges(name, row) {
|
|
30663
|
+
const def = this._cellValueChangesDefs[name];
|
|
30664
|
+
if (!def) {
|
|
30665
|
+
console.warn('[table] Listener with name {' + name + '} not registered! Please call registerCellValueChanges() before;');
|
|
30666
|
+
return;
|
|
30667
|
+
}
|
|
30668
|
+
// Stop previous subscription
|
|
30669
|
+
if (def.subscription) {
|
|
30670
|
+
def.subscription.unsubscribe();
|
|
30671
|
+
def.subscription = null;
|
|
30672
|
+
}
|
|
30673
|
+
else {
|
|
30674
|
+
if (this.debug)
|
|
30675
|
+
console.debug(`[table] Start values changes on row path {${def.formPath}}`);
|
|
30676
|
+
}
|
|
30677
|
+
// Listen value changes, and redirect to event emitter
|
|
30678
|
+
const control = row.validator && AppFormUtils.getControlFromPath(row.validator, def.formPath);
|
|
30679
|
+
if (!control) {
|
|
30680
|
+
console.warn(`[table] Could not listen cell changes: no validator or invalid form path {${def.formPath}}`);
|
|
30681
|
+
}
|
|
30682
|
+
else {
|
|
30683
|
+
def.subscription = control.valueChanges
|
|
30684
|
+
.pipe(
|
|
30685
|
+
// don't emit if control is disabled
|
|
30686
|
+
filter(() => control.enabled))
|
|
30687
|
+
.subscribe((value) => def.subject.next(value));
|
|
30688
|
+
// Emit the actual value
|
|
30689
|
+
if (def.emitInitialValue !== false) {
|
|
30690
|
+
def.subject.next(control.value);
|
|
30691
|
+
}
|
|
30692
|
+
}
|
|
30693
|
+
}
|
|
30694
|
+
stopCellValueChanges(name, destroy) {
|
|
30695
|
+
const def = this._cellValueChangesDefs[name];
|
|
30696
|
+
if (!def)
|
|
30697
|
+
return;
|
|
30698
|
+
if (def.subscription) {
|
|
30699
|
+
if (this.debug)
|
|
30700
|
+
console.debug('[table] Stop value changes on row path {' + def.formPath + '}');
|
|
30701
|
+
def.subscription.unsubscribe();
|
|
30702
|
+
def.subscription = null;
|
|
30703
|
+
}
|
|
30704
|
+
if (destroy && def.subject) {
|
|
30705
|
+
def.subject.complete();
|
|
30706
|
+
def.subject.unsubscribe();
|
|
30707
|
+
}
|
|
30708
|
+
}
|
|
30709
|
+
}
|
|
30710
|
+
AppAsyncTable.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AppAsyncTable, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
30711
|
+
AppAsyncTable.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: AppAsyncTable, inputs: { settingsId: "settingsId", debug: "debug", i18nColumnPrefix: "i18nColumnPrefix", i18nColumnSuffix: "i18nColumnSuffix", autoLoad: "autoLoad", readOnly: "readOnly", inlineEdition: "inlineEdition", focusFirstColumn: "focusFirstColumn", confirmBeforeDelete: "confirmBeforeDelete", confirmBeforeCancel: "confirmBeforeCancel", undoableDeletion: "undoableDeletion", saveBeforeDelete: "saveBeforeDelete", keepEditedRowOnSave: "keepEditedRowOnSave", saveBeforeSort: "saveBeforeSort", saveBeforeFilter: "saveBeforeFilter", propagateRowError: "propagateRowError", defaultSortBy: "defaultSortBy", defaultSortDirection: "defaultSortDirection", defaultPageSize: "defaultPageSize", defaultPageSizeOptions: "defaultPageSizeOptions", focusColumn: "focusColumn", dataSource: "dataSource", filter: "filter", disabled: "disabled", paginator: "paginator" }, outputs: { onRefresh: "onRefresh", onOpenRow: "onOpenRow", onNewRow: "onNewRow", onStartEditingRow: "onStartEditingRow", onConfirmEditCreateRow: "onConfirmEditCreateRow", onCancelOrDeleteRow: "onCancelOrDeleteRow", onBeforeDeleteRows: "onBeforeDeleteRows", onBeforeCancelRows: "onBeforeCancelRows", onBeforeSave: "onBeforeSave", onAfterDeletedRows: "onAfterDeletedRows", onSort: "onSort", onDirty: "onDirty", onError: "onError" }, viewQueries: [{ propertyName: "table", first: true, predicate: MatTable, descendants: true }, { propertyName: "childPaginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0 });
|
|
30712
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AppAsyncTable, decorators: [{
|
|
30713
|
+
type: Directive
|
|
30714
|
+
}], ctorParameters: function () { return [{ type: i0.Injector }, { type: undefined }, { type: EntitiesAsyncTableDataSource }, { type: undefined }]; }, propDecorators: { settingsId: [{
|
|
30715
|
+
type: Input
|
|
30716
|
+
}], debug: [{
|
|
30717
|
+
type: Input
|
|
30718
|
+
}], i18nColumnPrefix: [{
|
|
30719
|
+
type: Input
|
|
30720
|
+
}], i18nColumnSuffix: [{
|
|
30721
|
+
type: Input
|
|
30722
|
+
}], autoLoad: [{
|
|
30723
|
+
type: Input
|
|
30724
|
+
}], readOnly: [{
|
|
30725
|
+
type: Input
|
|
30726
|
+
}], inlineEdition: [{
|
|
30727
|
+
type: Input
|
|
30728
|
+
}], focusFirstColumn: [{
|
|
30729
|
+
type: Input
|
|
30730
|
+
}], confirmBeforeDelete: [{
|
|
30731
|
+
type: Input
|
|
30732
|
+
}], confirmBeforeCancel: [{
|
|
30733
|
+
type: Input
|
|
30734
|
+
}], undoableDeletion: [{
|
|
30735
|
+
type: Input
|
|
30736
|
+
}], saveBeforeDelete: [{
|
|
30737
|
+
type: Input
|
|
30738
|
+
}], keepEditedRowOnSave: [{
|
|
30739
|
+
type: Input
|
|
30740
|
+
}], saveBeforeSort: [{
|
|
30741
|
+
type: Input
|
|
30742
|
+
}], saveBeforeFilter: [{
|
|
30743
|
+
type: Input
|
|
30744
|
+
}], propagateRowError: [{
|
|
30745
|
+
type: Input
|
|
30746
|
+
}], defaultSortBy: [{
|
|
30747
|
+
type: Input
|
|
30748
|
+
}], defaultSortDirection: [{
|
|
30749
|
+
type: Input
|
|
30750
|
+
}], defaultPageSize: [{
|
|
30751
|
+
type: Input
|
|
30752
|
+
}], defaultPageSizeOptions: [{
|
|
30753
|
+
type: Input
|
|
30754
|
+
}], focusColumn: [{
|
|
30755
|
+
type: Input
|
|
30756
|
+
}], dataSource: [{
|
|
30757
|
+
type: Input
|
|
30758
|
+
}], filter: [{
|
|
30759
|
+
type: Input
|
|
30760
|
+
}], onRefresh: [{
|
|
30761
|
+
type: Output
|
|
30762
|
+
}], onOpenRow: [{
|
|
30763
|
+
type: Output
|
|
30764
|
+
}], onNewRow: [{
|
|
30765
|
+
type: Output
|
|
30766
|
+
}], onStartEditingRow: [{
|
|
30767
|
+
type: Output
|
|
30768
|
+
}], onConfirmEditCreateRow: [{
|
|
30769
|
+
type: Output
|
|
30770
|
+
}], onCancelOrDeleteRow: [{
|
|
30771
|
+
type: Output
|
|
30772
|
+
}], onBeforeDeleteRows: [{
|
|
30773
|
+
type: Output
|
|
30774
|
+
}], onBeforeCancelRows: [{
|
|
30775
|
+
type: Output
|
|
30776
|
+
}], onBeforeSave: [{
|
|
30777
|
+
type: Output
|
|
30778
|
+
}], onAfterDeletedRows: [{
|
|
30779
|
+
type: Output
|
|
30780
|
+
}], onSort: [{
|
|
30781
|
+
type: Output
|
|
30782
|
+
}], onDirty: [{
|
|
30783
|
+
type: Output
|
|
30784
|
+
}], onError: [{
|
|
30785
|
+
type: Output
|
|
30786
|
+
}], disabled: [{
|
|
30787
|
+
type: Input
|
|
30788
|
+
}], paginator: [{
|
|
30789
|
+
type: Input
|
|
30790
|
+
}], table: [{
|
|
30791
|
+
type: ViewChild,
|
|
30792
|
+
args: [MatTable, { static: false }]
|
|
30793
|
+
}], childPaginator: [{
|
|
30794
|
+
type: ViewChild,
|
|
30795
|
+
args: [MatPaginator, { static: false }]
|
|
30796
|
+
}], sort: [{
|
|
30797
|
+
type: ViewChild,
|
|
30798
|
+
args: [MatSort, { static: false }]
|
|
30799
|
+
}] } });
|
|
30800
|
+
|
|
28838
30801
|
// @dynamic
|
|
28839
30802
|
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|
28840
30803
|
class AppInMemoryTable extends AppTable {
|
|
@@ -33400,5 +35363,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
|
|
|
33400
35363
|
* Generated bundle index. Do not edit.
|
|
33401
35364
|
*/
|
|
33402
35365
|
|
|
33403
|
-
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_STORAGE, APP_TESTING_PAGES, APP_USER_EVENT_SERVICE, AboutModal, AbstractDateFormat, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AnimationState, AppAboutModalModule, AppAccountModule, AppAuthForm, AppAuthModal, AppAuthModule, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppRegisterModule, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayJoinPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, Fragments, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobModule, JobProgression, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBooleanField, MatChipsField, MatColorPipe, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItems, MenuOptions, MenuService, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NetworkService, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, NumpadTestPage, ObservableTestPage, OddPipe, PEER_URL_REGEXP, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, SCRYPT_PARAMS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageService, StrIncludesPipe, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TextForm, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEventModule, UserEventNotificationIcon, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, initArrayControlsFromValues, isAndroid, isBlankString, isCapacitor, isControlHasInput, isCordova, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isProgressEvent, isResponseEvent, isTouchUi, isWindows, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
35366
|
+
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_STORAGE, APP_TESTING_PAGES, APP_USER_EVENT_SERVICE, AboutModal, AbstractDateFormat, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AnimationState, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppRegisterModule, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayJoinPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, Fragments, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobModule, JobProgression, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBooleanField, MatChipsField, MatColorPipe, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItems, MenuOptions, MenuService, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NetworkService, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, NumpadTestPage, ObservableTestPage, OddPipe, PEER_URL_REGEXP, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, SCRYPT_PARAMS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageService, StrIncludesPipe, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TextForm, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEventModule, UserEventNotificationIcon, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, initArrayControlsFromValues, isAndroid, isBlankString, isCapacitor, isControlHasInput, isCordova, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isProgressEvent, isResponseEvent, isTouchUi, isWindows, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
33404
35367
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|