Przeglądaj źródła

fix(admin-ui): Do not run CanDeactivateGuard when switching tabs

Michael Bromley 6 lat temu
rodzic
commit
d8e62581ef

+ 5 - 0
admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts

@@ -17,6 +17,7 @@ import { normalizeString } from 'shared/normalize-string';
 import { CustomFieldConfig } from 'shared/shared-types';
 import { notNullOrUndefined } from 'shared/shared-utils';
 import { unique } from 'shared/unique';
+import { IGNORE_CAN_DEACTIVATE_GUARD } from 'src/app/shared/providers/routing/can-deactivate-detail-guard';
 
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import { createUpdatedTranslatable } from '../../../common/utilities/create-updated-translatable';
@@ -138,6 +139,9 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
     navigateToTab(tabName: TabName) {
         this.router.navigate(['./', { tab: tabName }], {
             relativeTo: this.route,
+            state: {
+                [IGNORE_CAN_DEACTIVATE_GUARD]: true,
+            },
         });
     }
 
@@ -315,6 +319,7 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
                     this.notificationService.success(_('common.notify-update-success'), {
                         entity: 'Product',
                     });
+                    this.changeDetector.markForCheck();
                 },
                 err => {
                     this.notificationService.error(_('common.notify-update-error'), {

+ 10 - 6
admin-ui/src/app/common/base-detail.component.ts

@@ -1,7 +1,7 @@
 import { FormGroup } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
+import { ActivatedRoute, Data, Router } from '@angular/router';
 import { combineLatest, Observable, of, Subject } from 'rxjs';
-import { map, shareReplay, switchMap, takeUntil, tap } from 'rxjs/operators';
+import { distinctUntilChanged, map, share, shareReplay, switchMap, takeUntil, tap } from 'rxjs/operators';
 import { LanguageCode } from 'shared/generated-types';
 import { CustomFieldConfig, CustomFields } from 'shared/shared-types';
 
@@ -26,8 +26,10 @@ export abstract class BaseDetailComponent<Entity extends { id: string }> {
 
     init() {
         this.entity$ = this.route.data.pipe(
-            switchMap(data => data.entity),
-            tap<any>(entity => (this.id = entity.id)),
+            switchMap<Data, Entity>(data => data.entity),
+            distinctUntilChanged((a, b) => a.id === b.id),
+            tap(entity => (this.id = entity.id)),
+            shareReplay(1),
         );
         this.isNew$ = this.entity$.pipe(
             map(entity => entity.id === ''),
@@ -36,14 +38,16 @@ export abstract class BaseDetailComponent<Entity extends { id: string }> {
         this.languageCode$ = this.route.paramMap.pipe(
             map(paramMap => paramMap.get('lang')),
             map(lang => (!lang ? getDefaultLanguage() : (lang as LanguageCode))),
+            distinctUntilChanged(),
+            shareReplay(1),
         );
 
         this.availableLanguages$ = this.serverConfigService.getAvailableLanguages();
 
         combineLatest(this.entity$, this.languageCode$)
             .pipe(takeUntil(this.destroy$))
-            .subscribe(([facet, languageCode]) => {
-                this.setFormValues(facet, languageCode);
+            .subscribe(([entity, languageCode]) => {
+                this.setFormValues(entity, languageCode);
                 this.detailForm.markAsPristine();
             });
     }

+ 1 - 1
admin-ui/src/app/shared/components/action-bar/action-bar.component.scss

@@ -7,6 +7,6 @@
     background-color: $color-grey-1;
     position: sticky;
     top: -24px;
-    z-index: 5;
+    z-index: 25;
     border-bottom: 1px solid $color-grey-3;
 }

+ 14 - 2
admin-ui/src/app/shared/providers/routing/can-deactivate-detail-guard.ts

@@ -1,5 +1,5 @@
 import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router';
+import { ActivatedRouteSnapshot, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router';
 import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { _ } from 'src/app/core/providers/i18n/mark-for-extraction';
@@ -7,9 +7,15 @@ import { _ } from 'src/app/core/providers/i18n/mark-for-extraction';
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import { ModalService } from '../modal/modal.service';
 
+/**
+ * When added to the [state object](https://angular.io/api/router/NavigationExtras#state), this will
+ * skip the CanDeactivateDetailGuard.
+ */
+export const IGNORE_CAN_DEACTIVATE_GUARD = 'IGNORE_CAN_DEACTIVATE_GUARD';
+
 @Injectable()
 export class CanDeactivateDetailGuard implements CanDeactivate<BaseDetailComponent<any>> {
-    constructor(private modalService: ModalService) {}
+    constructor(private modalService: ModalService, private router: Router) {}
 
     canDeactivate(
         component: BaseDetailComponent<any>,
@@ -17,6 +23,12 @@ export class CanDeactivateDetailGuard implements CanDeactivate<BaseDetailCompone
         currentState: RouterStateSnapshot,
         nextState?: RouterStateSnapshot,
     ): boolean | Observable<boolean> {
+        const nav = this.router.getCurrentNavigation();
+        if (nav) {
+            if (nav.extras.state && nav.extras.state[IGNORE_CAN_DEACTIVATE_GUARD] != null) {
+                return true;
+            }
+        }
         if (component.detailForm && component.detailForm.dirty) {
             return this.modalService
                 .dialog({