Есть ли способ получить защиту Angular Router, CanDeactivate, работающую как защита / интерфейс в компоненте формы? - PullRequest
0 голосов
/ 13 февраля 2019

Я читал об использовании CanDeactivate, чтобы определить, покидает ли пользователь форму / маршрут, и это то, что мне нужно в моем проекте.Я попытался реализовать этот SO-ответ: SO-ответ

Когда я просматриваю свой компонент пользовательской формы, я вводю информацию в один из входов, а затем пытаюсь обновить, нажимая на другой компонент,переход на google.com через URL браузера.Ни один из этих триггеров не вызывает запрос подтверждения в этом ответе.

Моя структура проекта настроена так:

> src
 > app
  - app-routing.module.ts
  - app.module.ts
  - app.component.ts
  - app.component.html
  > auth
   > ...
  > dashboard
   - dashboard.module.ts
   - dashboard.component.ts
   - dashboard.component.html
   > components
    > forms
     - user-form.component.ts
     - user-form.component.html
    > ...
  > shared
   > guards
    - pending-changes.guard.ts
   > ...

Я уже пытался реализовать этот SO-ответ: SO-ответ

В моем модуле панели мониторинга я импортировал свой pending-changes.guard и определил один маршрут для моего компонента панели мониторинга, который использует мой canDeactivate: [PendingChangesGuard] Затем я добавляю PendingChangesGuard в качестве провайдера в мой NgModule для моей панели мониторинга.module.

В моем компоненте пользовательской формы я импортирую ComponentCanDeactivate из своего PendingChangesGuard в shared, а затем реализую интерфейс ComponentCanDeactivate в компоненте.Я определяю функцию canDeactivate (), которая проверяет, является ли моя пользовательская форма типа FormGroup грязной, и если она грязная, я возвращаю false, которая должна показать подтверждение пользователю.Если форма не использовалась, я хочу вернуть true.

Мой маршрут, определенный в модуле панели мониторинга, предназначен для компонента панели мониторинга.Мой компонент панели мониторинга является родителем другого компонента моего компонента пользовательской формы.Таким образом, мой компонент пользовательской формы, который содержит html для моей формы, специально не имеет своего собственного url / path / route / routerLink, настроенного для того, когда пользователь отображает этот компонент, щелкая другие компоненты.Я не уверен, что эта настройка здесь правильная, потому что большинство примеров, которые я вижу, имеют параметр route / path / routerLink, специально настроенный для компонента формы, который будет содержать несохраненные данные, которые вызовут приглашение пользователя.

pending-changes.guard.ts

import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

export interface ComponentCanDeactivate {
  canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
  canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
    // if there are no pending changes, just allow deactivation; else confirm first
    return component.canDeactivate() ?
      true :
      // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
      // when navigating away from your angular app, the browser will show a generic warning message
      // see http://stackoverflow.com/a/42207299/7307355
      confirm('WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
  }
}

dashboard.module.ts

import { PendingChangesGuard } from '../shared/guards/pending-changes.guard';

export const routes: Routes = [
  {
    path: '',
    component: DashboardComponent,
    canDeactivate: [PendingChangesGuard]
  }
];

@NgModule({
  imports: [
    CodemirrorModule,
    CommonModule,
    CronEditorModule,
    FormsModule,
    ReactiveFormsModule,
    SimpleNotificationsModule.forRoot(),
    NgbModule,
    NgxsModule.forFeature(STATES),
    NgxSpinnerModule,
    RouterModule.forChild(routes),
    SharedModule
  ],
  declarations: COMPONENTS,
  exports: COMPONENTS,
  entryComponents: [ProfileComponent],
  providers: [PendingChangesGuard]
})
export class DashboardModule {}

user-form.component.ts

import { ComponentCanDeactivate } from '../../../../shared/guards/pending-changes.guard';
@Component({
  selector: 'user-form',
  templateUrl: './user-form.component.html',
  styleUrls: ['./user-form.component.scss']
})
export class UserFormComponent implements OnInit, ComponentCanDeactivate {
  ...

  userForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  // @HostListener allows us to also guard against browser refresh, close, etc.
  @HostListener('window:beforeunload')
  canDeactivate(): Observable<boolean> | boolean {
    // insert logic to check if there are pending changes here;
    if (this.userForm.dirty === true) return false;
    return true;
    // returning true will navigate without confirmation
    // returning false will show a confirm dialog before navigating away
  }

  ...
}

Когда я просматриваю свой компонент пользовательской формы, я ввожу информацию в один из входов и затем пытаюсь обновить, нажимая на другой компонент, переходя к google.com через URL браузера.Ничто из этого не вызывает запрос подтверждения в моем PendingChangesGuard.Я ожидаю, что пользователю будет предложено, если у него есть несохраненные данные в компоненте пользовательской формы.

ОБНОВЛЕНИЕ: у меня появляется подтверждение для обновления и навигации по URL браузера, но это не покрывает все мои потребностиЯ думаю, что эта функциональность происходит в основном от @HostListener.Похоже, это не точное сообщение, которое, как я думал, я отправлял своей гвардии.Я где-то читал, что браузеры обрабатывают эти типы подтверждений самостоятельно, поэтому я не слишком удивлен, но, возможно, это означает, что я должен обработать это с помощью своего собственного пользовательского модального режима?Я использую Chrome версии 70.0.3538.77 (Официальная сборка) (64-разрядная версия) для тестирования моего пользовательского интерфейса.

Некоторые другие проблемы, о которых я думаю, это то, что у меня есть этот большой компонент, состоящий из других компонентов,Проблема заключается в том, что я и первоначальный владелец проекта (я новичок в проекте, и теперь он больше не в компании, и я все еще нахожусь под углом) никогда не думал о маршрутизации компонентов за компонентом панели мониторинга, потому что мы решили, что отображатьв каждом компоненте через управление состоянием NGXS.Обычно компоненты верхнего уровня (вкладки навигации на приборной панели) имеют @inputs и @outputs, чтобы позволить данным проходить вверх / вниз через компоненты из состояния, в котором состояние получает свои данные из действий NGXS, которые выполняют вызовы API и помещают данные вмодель состояний, которая имеет смысл для отображения в компоненте панели мониторинга.

И поскольку у меня нет маршрутизации для каждого компонента, если пользователь нажимает кнопку, вызывающую отображение другого компонента вместо моего компонента пользовательской формы черезngSwitch, тогда я не получаю подтверждение несохраненных данных в моем компоненте пользовательской формы, потому что я не "технически" перемещаюсь "или изменяю маршрут.

Так как мне организовать этот проект?Добавлять ли мне маршруты к каждому компоненту в маршрутах моего модуля Dashboard, даже если у моего модуля Dashboard есть загруженные с отложенным доступом дети из app-routing.module.ts?Или мне нужно встроить хуки во все, что можно нажимать, когда в форме проверять грязное значение формы?Или есть более простой способ справиться с этим из компонента пользовательской формы, например, с помощью ловушки жизненного цикла (на ум приходит OnDestroy (), но я думаю, что это слишком поздно в жизненном цикле для решения этой проблемы)?

*У 1045 * app-routing.module.ts есть эти маршруты

const routes: Routes = [
  {
    path: 'dashboard',
    canActivate: [AuthGuard],
    loadChildren: './dashboard/dashboard.module#DashboardModule'
  },
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full'
  }
];
...