@ecodev/natural 58.0.2 → 58.0.4
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/esm2022/lib/classes/abstract-detail.mjs +12 -20
- package/esm2022/lib/classes/cumulative-changes.mjs +15 -13
- package/esm2022/lib/modules/common/directives/linkable-tab.directive.mjs +5 -6
- package/esm2022/lib/modules/relations/relations.component.mjs +4 -5
- package/esm2022/lib/modules/sidenav/sidenav.service.mjs +9 -10
- package/esm2022/lib/services/abstract-model.service.mjs +28 -13
- package/fesm2022/ecodev-natural.mjs +67 -61
- package/fesm2022/ecodev-natural.mjs.map +1 -1
- package/lib/classes/abstract-detail.d.ts +8 -0
- package/lib/classes/cumulative-changes.d.ts +2 -1
- package/lib/modules/common/directives/linkable-tab.directive.d.ts +1 -1
- package/lib/modules/relations/relations.component.d.ts +1 -1
- package/lib/modules/sidenav/sidenav.service.d.ts +1 -1
- package/lib/services/abstract-model.service.d.ts +19 -2
- package/package.json +1 -1
|
@@ -2302,14 +2302,16 @@ function money(control) {
|
|
|
2302
2302
|
* Cumulate all changes made to an object over time
|
|
2303
2303
|
*/
|
|
2304
2304
|
class CumulativeChanges {
|
|
2305
|
-
|
|
2306
|
-
|
|
2305
|
+
constructor() {
|
|
2306
|
+
this.original = {};
|
|
2307
|
+
this.diff = {};
|
|
2308
|
+
}
|
|
2307
2309
|
/**
|
|
2308
2310
|
* Initialize the original values, should be called exactly one time per instance
|
|
2309
2311
|
*/
|
|
2310
2312
|
initialize(originalValues) {
|
|
2311
|
-
this
|
|
2312
|
-
this
|
|
2313
|
+
this.original = cloneDeep(originalValues);
|
|
2314
|
+
this.diff = {};
|
|
2313
2315
|
}
|
|
2314
2316
|
/**
|
|
2315
2317
|
* Returns a literal that contains only the keys whose values have been changed by this call or any previous calls.
|
|
@@ -2323,25 +2325,25 @@ class CumulativeChanges {
|
|
|
2323
2325
|
*/
|
|
2324
2326
|
differences(newValues) {
|
|
2325
2327
|
Object.keys(newValues).forEach(key => {
|
|
2326
|
-
if (key in this
|
|
2328
|
+
if (key in this.diff ||
|
|
2327
2329
|
(newValues[key] !== undefined &&
|
|
2328
|
-
(!(key in this
|
|
2329
|
-
this
|
|
2330
|
+
(!(key in this.original) || !isEqual(this.original[key], newValues[key])))) {
|
|
2331
|
+
this.diff[key] = newValues[key];
|
|
2330
2332
|
}
|
|
2331
2333
|
});
|
|
2332
|
-
return Object.keys(this
|
|
2334
|
+
return Object.keys(this.diff).length ? this.diff : null;
|
|
2333
2335
|
}
|
|
2334
2336
|
/**
|
|
2335
2337
|
* Commit the given new values, so they are not treated as differences anymore.
|
|
2336
2338
|
*/
|
|
2337
2339
|
commit(newValues) {
|
|
2338
|
-
this
|
|
2339
|
-
...this
|
|
2340
|
+
this.original = {
|
|
2341
|
+
...this.original,
|
|
2340
2342
|
...cloneDeep(newValues),
|
|
2341
2343
|
};
|
|
2342
2344
|
Object.keys(newValues).forEach(key => {
|
|
2343
|
-
if (key in this
|
|
2344
|
-
delete this
|
|
2345
|
+
if (key in this.diff && isEqual(this.diff[key], newValues[key])) {
|
|
2346
|
+
delete this.diff[key];
|
|
2345
2347
|
}
|
|
2346
2348
|
});
|
|
2347
2349
|
}
|
|
@@ -2355,14 +2357,6 @@ function isNaturalDialogTriggerProvidedData(dialogData) {
|
|
|
2355
2357
|
}
|
|
2356
2358
|
// @dynamic
|
|
2357
2359
|
class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
2358
|
-
#dialogData;
|
|
2359
|
-
/**
|
|
2360
|
-
* Once set, this must not change anymore, especially not right after the creation mutation,
|
|
2361
|
-
* so the form does not switch from creation mode to update mode without an actual reload of
|
|
2362
|
-
* model from DB (by navigating to update page).
|
|
2363
|
-
*/
|
|
2364
|
-
#isUpdatePage;
|
|
2365
|
-
#changes;
|
|
2366
2360
|
constructor(key, service) {
|
|
2367
2361
|
super();
|
|
2368
2362
|
this.key = key;
|
|
@@ -2401,14 +2395,14 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2401
2395
|
* Injected service
|
|
2402
2396
|
*/
|
|
2403
2397
|
this.route = inject(ActivatedRoute);
|
|
2404
|
-
this
|
|
2398
|
+
this._dialogData = inject(MAT_DIALOG_DATA, { optional: true });
|
|
2405
2399
|
/**
|
|
2406
2400
|
* Once set, this must not change anymore, especially not right after the creation mutation,
|
|
2407
2401
|
* so the form does not switch from creation mode to update mode without an actual reload of
|
|
2408
2402
|
* model from DB (by navigating to update page).
|
|
2409
2403
|
*/
|
|
2410
|
-
this
|
|
2411
|
-
this
|
|
2404
|
+
this._isUpdatePage = false;
|
|
2405
|
+
this.changes = new CumulativeChanges();
|
|
2412
2406
|
}
|
|
2413
2407
|
/**
|
|
2414
2408
|
* You probably should not override this method. Instead, consider overriding `initForm()`.
|
|
@@ -2418,8 +2412,8 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2418
2412
|
this.initForm();
|
|
2419
2413
|
}
|
|
2420
2414
|
else {
|
|
2421
|
-
const route = isNaturalDialogTriggerProvidedData(this
|
|
2422
|
-
? this
|
|
2415
|
+
const route = isNaturalDialogTriggerProvidedData(this._dialogData)
|
|
2416
|
+
? this._dialogData.activatedRoute
|
|
2423
2417
|
: this.route;
|
|
2424
2418
|
this.#subscribeToModelFromResolvedData(route);
|
|
2425
2419
|
}
|
|
@@ -2455,7 +2449,7 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2455
2449
|
* This should be used instead of checking `data.model.id` directly, in order to type guard and get proper typing
|
|
2456
2450
|
*/
|
|
2457
2451
|
isUpdatePage() {
|
|
2458
|
-
return this
|
|
2452
|
+
return this._isUpdatePage;
|
|
2459
2453
|
}
|
|
2460
2454
|
/**
|
|
2461
2455
|
* Update the object on the server with the values from the form fields that were modified since
|
|
@@ -2471,15 +2465,15 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2471
2465
|
ifValid(this.form).subscribe(() => {
|
|
2472
2466
|
const newValues = this.form.getRawValue();
|
|
2473
2467
|
if (submitAllFields) {
|
|
2474
|
-
this
|
|
2468
|
+
this.changes.initialize({});
|
|
2475
2469
|
}
|
|
2476
2470
|
const toSubmit = {
|
|
2477
2471
|
id: this.data.model.id,
|
|
2478
|
-
...this
|
|
2472
|
+
...this.changes.differences(newValues),
|
|
2479
2473
|
};
|
|
2480
2474
|
const update = now ? this.service.updateNow(toSubmit) : this.service.update(toSubmit);
|
|
2481
2475
|
update.subscribe(model => {
|
|
2482
|
-
this
|
|
2476
|
+
this.changes.commit(newValues);
|
|
2483
2477
|
this.alertService.info($localize `Mis à jour`);
|
|
2484
2478
|
this.postUpdate(model);
|
|
2485
2479
|
});
|
|
@@ -2565,9 +2559,9 @@ class NaturalAbstractDetail extends NaturalAbstractPanel {
|
|
|
2565
2559
|
* will incorrectly be called exactly 1 time per component instance, even if the object changes via route navigation.
|
|
2566
2560
|
*/
|
|
2567
2561
|
initForm() {
|
|
2568
|
-
this
|
|
2562
|
+
this._isUpdatePage = !!this.data.model.id;
|
|
2569
2563
|
this.form = this.service.getFormGroup(this.data.model);
|
|
2570
|
-
this
|
|
2564
|
+
this.changes.initialize(this.form.getRawValue());
|
|
2571
2565
|
}
|
|
2572
2566
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: NaturalAbstractDetail, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2573
2567
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.5", type: NaturalAbstractDetail, usesInheritance: true, ngImport: i0 }); }
|
|
@@ -4538,21 +4532,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImpor
|
|
|
4538
4532
|
}] });
|
|
4539
4533
|
|
|
4540
4534
|
class NaturalAbstractModelService {
|
|
4541
|
-
|
|
4542
|
-
|
|
4535
|
+
/**
|
|
4536
|
+
*
|
|
4537
|
+
* @param name service and single object query name (eg. userForFront or user).
|
|
4538
|
+
* @param oneQuery GraphQL query to fetch a single object from ID (eg. userForCrudQuery).
|
|
4539
|
+
* @param allQuery GraphQL query to fetch a filtered list of objects (eg. usersForCrudQuery).
|
|
4540
|
+
* @param createMutation GraphQL mutation to create an object.
|
|
4541
|
+
* @param updateMutation GraphQL mutation to update an object.
|
|
4542
|
+
* @param deleteMutation GraphQL mutation to delete a list of objects.
|
|
4543
|
+
* @param plural list query name (eg. usersForFront or users).
|
|
4544
|
+
* @param createName create object mutation name (eg. createUser).
|
|
4545
|
+
* @param updateName update object mutation name (eg. updateUser).
|
|
4546
|
+
* @param deleteName delete object mutation name (eg. deleteUsers).
|
|
4547
|
+
*/
|
|
4548
|
+
constructor(name, oneQuery, allQuery, createMutation, updateMutation, deleteMutation, plural = null, createName = null, updateName = null, deleteName = null) {
|
|
4543
4549
|
this.name = name;
|
|
4544
4550
|
this.oneQuery = oneQuery;
|
|
4545
4551
|
this.allQuery = allQuery;
|
|
4546
4552
|
this.createMutation = createMutation;
|
|
4547
4553
|
this.updateMutation = updateMutation;
|
|
4548
4554
|
this.deleteMutation = deleteMutation;
|
|
4555
|
+
this.createName = createName;
|
|
4556
|
+
this.updateName = updateName;
|
|
4557
|
+
this.deleteName = deleteName;
|
|
4549
4558
|
/**
|
|
4550
4559
|
* Store the creation mutations that are pending
|
|
4551
4560
|
*/
|
|
4552
4561
|
this.creatingCache = new Map();
|
|
4553
4562
|
this.apollo = inject(Apollo);
|
|
4554
4563
|
this.naturalDebounceService = inject(NaturalDebounceService);
|
|
4555
|
-
this
|
|
4564
|
+
this.plural = plural ?? makePlural(this.name);
|
|
4556
4565
|
}
|
|
4557
4566
|
/**
|
|
4558
4567
|
* List of individual fields validators
|
|
@@ -4648,7 +4657,7 @@ class NaturalAbstractModelService {
|
|
|
4648
4657
|
* You must subscribe to start getting results (and fetch from network).
|
|
4649
4658
|
*/
|
|
4650
4659
|
getOne(id) {
|
|
4651
|
-
return this
|
|
4660
|
+
return this.prepareOneQuery(id, 'cache-and-network').pipe(takeWhile(result => result.networkStatus !== NetworkStatus.ready, true), map(result => result.data[this.name]));
|
|
4652
4661
|
}
|
|
4653
4662
|
/**
|
|
4654
4663
|
* Watch a single object
|
|
@@ -4661,9 +4670,9 @@ class NaturalAbstractModelService {
|
|
|
4661
4670
|
* You **MUST** unsubscribe.
|
|
4662
4671
|
*/
|
|
4663
4672
|
watchOne(id, fetchPolicy = 'cache-and-network') {
|
|
4664
|
-
return this
|
|
4673
|
+
return this.prepareOneQuery(id, fetchPolicy).pipe(map(result => result.data[this.name]));
|
|
4665
4674
|
}
|
|
4666
|
-
|
|
4675
|
+
prepareOneQuery(id, fetchPolicy) {
|
|
4667
4676
|
this.throwIfObservable(id);
|
|
4668
4677
|
this.throwIfNotQuery(this.oneQuery);
|
|
4669
4678
|
return this.getVariablesForOne(id).pipe(switchMap(variables => {
|
|
@@ -4890,11 +4899,11 @@ class NaturalAbstractModelService {
|
|
|
4890
4899
|
* This is used for the unique validator
|
|
4891
4900
|
*/
|
|
4892
4901
|
count(queryVariablesManager) {
|
|
4893
|
-
const queryName = 'Count' + upperCaseFirstLetter(this
|
|
4902
|
+
const queryName = 'Count' + upperCaseFirstLetter(this.plural);
|
|
4894
4903
|
const filterType = upperCaseFirstLetter(this.name) + 'Filter';
|
|
4895
4904
|
const query = gql `
|
|
4896
4905
|
query ${queryName} ($filter: ${filterType}) {
|
|
4897
|
-
count: ${this
|
|
4906
|
+
count: ${this.plural} (filter: $filter, pagination: {pageSize: 0, pageIndex: 0}) {
|
|
4898
4907
|
length
|
|
4899
4908
|
}
|
|
4900
4909
|
}`;
|
|
@@ -4937,27 +4946,27 @@ class NaturalAbstractModelService {
|
|
|
4937
4946
|
* This is used to extract only the array of fetched objects out of the entire fetched data
|
|
4938
4947
|
*/
|
|
4939
4948
|
mapAll() {
|
|
4940
|
-
return map(result => result.data[this
|
|
4949
|
+
return map(result => result.data[this.plural]); // See https://github.com/apollographql/apollo-client/issues/5662
|
|
4941
4950
|
}
|
|
4942
4951
|
/**
|
|
4943
4952
|
* This is used to extract only the created object out of the entire fetched data
|
|
4944
4953
|
*/
|
|
4945
4954
|
mapCreation(result) {
|
|
4946
|
-
const name = 'create' + upperCaseFirstLetter(this.name);
|
|
4955
|
+
const name = this.createName ?? 'create' + upperCaseFirstLetter(this.name);
|
|
4947
4956
|
return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
|
|
4948
4957
|
}
|
|
4949
4958
|
/**
|
|
4950
4959
|
* This is used to extract only the updated object out of the entire fetched data
|
|
4951
4960
|
*/
|
|
4952
4961
|
mapUpdate(result) {
|
|
4953
|
-
const name = 'update' + upperCaseFirstLetter(this.name);
|
|
4962
|
+
const name = this.updateName ?? 'update' + upperCaseFirstLetter(this.name);
|
|
4954
4963
|
return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
|
|
4955
4964
|
}
|
|
4956
4965
|
/**
|
|
4957
4966
|
* This is used to extract only flag when deleting an object
|
|
4958
4967
|
*/
|
|
4959
4968
|
mapDelete(result) {
|
|
4960
|
-
const name = 'delete' + upperCaseFirstLetter(this
|
|
4969
|
+
const name = this.deleteName ?? 'delete' + upperCaseFirstLetter(this.plural);
|
|
4961
4970
|
return result.data[name]; // See https://github.com/apollographql/apollo-client/issues/5662
|
|
4962
4971
|
}
|
|
4963
4972
|
/**
|
|
@@ -5534,7 +5543,6 @@ function getTabId(tab) {
|
|
|
5534
5543
|
* </mat-tab-group>
|
|
5535
5544
|
*/
|
|
5536
5545
|
class NaturalLinkableTabDirective extends NaturalAbstractController {
|
|
5537
|
-
#isLoadingRouteConfig;
|
|
5538
5546
|
constructor(component, route, router) {
|
|
5539
5547
|
super();
|
|
5540
5548
|
this.component = component;
|
|
@@ -5544,13 +5552,13 @@ class NaturalLinkableTabDirective extends NaturalAbstractController {
|
|
|
5544
5552
|
* If false, disables the persistent navigation
|
|
5545
5553
|
*/
|
|
5546
5554
|
this.naturalLinkableTab = true;
|
|
5547
|
-
this
|
|
5555
|
+
this.isLoadingRouteConfig = false;
|
|
5548
5556
|
router.events.pipe(takeUntilDestroyed()).subscribe(event => {
|
|
5549
5557
|
if (event instanceof RouteConfigLoadStart) {
|
|
5550
|
-
this
|
|
5558
|
+
this.isLoadingRouteConfig = true;
|
|
5551
5559
|
}
|
|
5552
5560
|
else if (event instanceof RouteConfigLoadEnd) {
|
|
5553
|
-
this
|
|
5561
|
+
this.isLoadingRouteConfig = false;
|
|
5554
5562
|
}
|
|
5555
5563
|
});
|
|
5556
5564
|
}
|
|
@@ -5570,7 +5578,7 @@ class NaturalLinkableTabDirective extends NaturalAbstractController {
|
|
|
5570
5578
|
});
|
|
5571
5579
|
// When mat-tab-groups selected tab change, update url
|
|
5572
5580
|
this.component.selectedTabChange.pipe(takeUntil$1(this.ngUnsubscribe)).subscribe(event => {
|
|
5573
|
-
if (this
|
|
5581
|
+
if (this.isLoadingRouteConfig) {
|
|
5574
5582
|
return;
|
|
5575
5583
|
}
|
|
5576
5584
|
const activatedTabName = getTabId(event.tab);
|
|
@@ -9869,14 +9877,13 @@ const fallbackIfNoOpenedPanels = (segments) => {
|
|
|
9869
9877
|
* </natural-relations>
|
|
9870
9878
|
*/
|
|
9871
9879
|
class NaturalRelationsComponent extends NaturalAbstractController {
|
|
9872
|
-
#service;
|
|
9873
9880
|
get service() {
|
|
9874
|
-
return this
|
|
9881
|
+
return this._service;
|
|
9875
9882
|
}
|
|
9876
9883
|
set service(service) {
|
|
9877
|
-
this
|
|
9884
|
+
this._service = service;
|
|
9878
9885
|
this.loading = true;
|
|
9879
|
-
const items$ = this
|
|
9886
|
+
const items$ = this._service.watchAll(this.variablesManager).pipe(takeUntil(this.ngUnsubscribe), tap({
|
|
9880
9887
|
next: () => (this.loading = false),
|
|
9881
9888
|
complete: () => (this.loading = false),
|
|
9882
9889
|
error: () => (this.loading = false),
|
|
@@ -10303,7 +10310,6 @@ function assert(value) {
|
|
|
10303
10310
|
* Maybe the better is to wait next release
|
|
10304
10311
|
*/
|
|
10305
10312
|
class NaturalSidenavService extends NaturalAbstractController {
|
|
10306
|
-
#isMobileView;
|
|
10307
10313
|
constructor(breakpointObserver, router, sessionStorage, naturalSidenavStackService) {
|
|
10308
10314
|
super();
|
|
10309
10315
|
this.breakpointObserver = breakpointObserver;
|
|
@@ -10343,7 +10349,7 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10343
10349
|
this.openedStorageKey = 'menu-opened';
|
|
10344
10350
|
this.minimizedStorageKeyWithName = null;
|
|
10345
10351
|
this.openedStorageKeyWithName = null;
|
|
10346
|
-
this
|
|
10352
|
+
this._isMobileView = false;
|
|
10347
10353
|
}
|
|
10348
10354
|
get activeMode() {
|
|
10349
10355
|
return this.mode;
|
|
@@ -10373,8 +10379,8 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10373
10379
|
.observe([Breakpoints.XSmall, Breakpoints.Small])
|
|
10374
10380
|
.pipe(takeUntil$1(this.ngUnsubscribe))
|
|
10375
10381
|
.subscribe(r => {
|
|
10376
|
-
this
|
|
10377
|
-
const isBig = !this
|
|
10382
|
+
this._isMobileView = r.matches;
|
|
10383
|
+
const isBig = !this._isMobileView;
|
|
10378
10384
|
this.mode = isBig ? this.modes[0] : this.modes[1];
|
|
10379
10385
|
if (oldIsBig === null || isBig !== oldIsBig) {
|
|
10380
10386
|
oldIsBig = isBig;
|
|
@@ -10399,13 +10405,13 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10399
10405
|
}
|
|
10400
10406
|
}
|
|
10401
10407
|
isMobileView() {
|
|
10402
|
-
return this
|
|
10408
|
+
return this._isMobileView;
|
|
10403
10409
|
}
|
|
10404
10410
|
/**
|
|
10405
10411
|
* Close nav on mobile view after a click
|
|
10406
10412
|
*/
|
|
10407
10413
|
navItemClicked() {
|
|
10408
|
-
if (this
|
|
10414
|
+
if (this._isMobileView) {
|
|
10409
10415
|
this.close();
|
|
10410
10416
|
}
|
|
10411
10417
|
}
|
|
@@ -10442,7 +10448,7 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10442
10448
|
assert(this.openedStorageKeyWithName);
|
|
10443
10449
|
const value = this.sessionStorage.getItem(this.openedStorageKeyWithName);
|
|
10444
10450
|
if (value === null) {
|
|
10445
|
-
return !this
|
|
10451
|
+
return !this._isMobileView;
|
|
10446
10452
|
}
|
|
10447
10453
|
else {
|
|
10448
10454
|
return value === 'true';
|
|
@@ -10463,10 +10469,10 @@ class NaturalSidenavService extends NaturalAbstractController {
|
|
|
10463
10469
|
}
|
|
10464
10470
|
setOpened(value) {
|
|
10465
10471
|
this.opened = value;
|
|
10466
|
-
if (this.opened && this
|
|
10472
|
+
if (this.opened && this._isMobileView) {
|
|
10467
10473
|
this.minimized = false;
|
|
10468
10474
|
}
|
|
10469
|
-
else if (!this
|
|
10475
|
+
else if (!this._isMobileView) {
|
|
10470
10476
|
assert(this.openedStorageKeyWithName);
|
|
10471
10477
|
this.sessionStorage.setItem(this.openedStorageKeyWithName, this.opened ? 'true' : 'false');
|
|
10472
10478
|
}
|