Это моя реализация, чтобы получить диалоговое окно подтверждения перед тем, как покинуть определенный маршрут, используя диалоговое окно ngx-bootstrap. У меня есть глобальная переменная под названием «canNavigate» с помощью службы. Эта переменная будет содержать логическое значение, если оно истинно или ложно, чтобы увидеть, возможна ли навигация. Это значение изначально истинно, но если я сделаю изменение в своем компоненте, я сделаю его ложным, поэтому 'canNavigate' будет ложным. Если значение равно false, я открою диалоговое окно, и если пользователь отменит изменения, он пойдет по желаемому маршруту, также приняв queryParams, иначе он не будет маршрутизировать.

м приложении Angular 4 у меня есть несколько компонентов с формой, например:

export class MyComponent implements OnInit, FormComponent {

  form: FormGroup;

  ngOnInit() {
    this.form = new FormGroup({...});
  }

они используют службу Guard, чтобы предотвратить потерю неподтвержденных изменений, поэтому, если пользователь пытается изменить маршрут до того, как он запросит подтверждение:

import { CanDeactivate } from '@angular/router';
import { FormGroup } from '@angular/forms';

export interface FormComponent {
  form: FormGroup;
}

export class UnsavedChangesGuardService implements CanDeactivate<FormComponent> {
  canDeactivate(component: FormComponent) {
    if (component.form.dirty) {
      return confirm(
        'The form has not been submitted yet, do you really want to leave page?'
      );
    }

    return true;
  }
}

Это с помощью простогоconfirm(...) диалог, и он работает просто отлично.

Однако я хотел бы заменить этот простой диалог более модным диалоговым окном, например, используяngx-bootstrap Модал.

Как я могу достичь того же результата, используя вместо этого модальное?

 jbrown26 сент. 2017 г., 20:26
Я использовал несколько разных модалов и всегда обнаруживал, что лучший подход - это использовать сервис с компонентом. Довольно простые вещи. Какая часть вам неясна?
 Francesco Borzi27 сент. 2017 г., 14:56
@jbrown этот модал требует jQuery, который я бы не стал включать ...
 jbrown26 сент. 2017 г., 21:15
Таким образом, возвращаемый тип canDeactivate должен быть Observable <boolean> | Promise <boolean> | boolean. Итак, я бы искал модал, где результат возвращает один из них. Проверь это ...github.com/dougludlow/ng2-bs3-modal
 Francesco Borzi26 сент. 2017 г., 21:05
околоcanDeactivate возврат значения на основе ввода пользователя

Ответы на вопрос(3)

кажется, стоит упомянуть, что вам придется покрывать и увольнение модального режима, потому что метод action () может не срабатывать (например, если вы разрешите кнопку esc или щелчок за пределами модального режима) , В этом случае наблюдаемое никогда не завершается, и ваше приложение может застрять, если вы используете его для маршрутизации.

Я добился этого, подписавшись на bsModalServiceonHide Собственность и слияние этого и предмета действия вместе:

confirmModal(text?: string): Observable<boolean> {
    const subject = new Subject<boolean>();
    const modal = this.modalService.show(ConfirmLeaveModalComponent);
    modal.content.subject = subject;
    modal.content.text = text ? text : 'Are you sure?';
    const onHideObservable = this.modalService.onHide.map(() => false);
    return merge(
      subject.asObservable(),
      onHideObservable
    );
  }

В моем случае я сопоставляюonHide Наблюдаемый ложным, потому что увольнение в моем случае считается прерыванием (только щелчок «да» приведет к положительному результату для моего модального подтверждения).

Решение Вопроса

Модальности ngx-bootstrap а такжеRxJs Предметы.

Прежде всего я создал модальный компонент:

import { Component } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { BsModalRef } from 'ngx-bootstrap';

@Component({
  selector: 'app-confirm-leave',
  templateUrl: './confirm-leave.component.html',
  styleUrls: ['./confirm-leave.component.scss']
})
export class ConfirmLeaveComponent {

  subject: Subject<boolean>;

  constructor(public bsModalRef: BsModalRef) { }

  action(value: boolean) {
    this.bsModalRef.hide();
    this.subject.next(value);
    this.subject.complete();
  }
}

вот шаблон:

<div class="modal-header modal-block-primary">
  <button type="button" class="close" (click)="bsModalRef.hide()">
    <span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
  </button>
  <h4 class="modal-title">Are you sure?</h4>
</div>
<div class="modal-body clearfix">
  <div class="modal-icon">
    <i class="fa fa-question-circle"></i>
  </div>
  <div class="modal-text">
    <p>The form has not been submitted yet, do you really want to leave page?</p>
  </div>
</div>
<div class="modal-footer">
  <button class="btn btn-default" (click)="action(false)">No</button>
  <button class="btn btn-primary right" (click)="action(true)">Yes</button>
</div>

Затем я изменил свою охрану, используя тему, теперь это выглядит так:

import { CanDeactivate } from '@angular/router';
import { FormGroup } from '@angular/forms';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { BsModalService } from 'ngx-bootstrap';

import { ConfirmLeaveComponent } from '.....';

export interface FormComponent {
  form: FormGroup;
}

@Injectable()
export class UnsavedChangesGuardService implements CanDeactivate<FormComponent> {

  constructor(private modalService: BsModalService) {}

  canDeactivate(component: FormComponent) {
    if (component.form.dirty) {
      const subject = new Subject<boolean>();

      const modal = this.modalService.show(ConfirmLeaveComponent, {'class': 'modal-dialog-primary'});
      modal.content.subject = subject;

      return subject.asObservable();
    }

    return true;
  }
}

В файле app.module.ts перейдите в раздел @NgModule и добавьте компонент ConfirmLeaveComponent в entryComponents.

@NgModule({
  entryComponents: [
    ConfirmLeaveComponent,
  ]
})
 Andrew Lobban04 мая 2018 г., 17:12
Этот пример также работает с Angular Material, следуя реализации Subject!
 Andrew Lobban04 мая 2018 г., 22:04
Я хотел бы понять, почему возврат Observable в метод CanDeactivate () работает?
 Andrew Lobban24 мая 2018 г., 19:50
@ Khush спасибо за объяснение. Я думал, что это было намного сложнее, чем это.
 Francesco Borzi01 февр. 2019 г., 10:39
@KushanRandima это происходит потому, что проверкаif (component.form.dirty)вы можете изменить его и реализовать свою собственную логику, чтобы сравнить старое значение формы и новое значение формы. Насколько я знаю, в Angular такой встроенной функциональности нет.
 khush24 мая 2018 г., 09:34
Метод @AndrewLobban canDeactivate в UnsavedChangeGuardService переопределяет метод из интерфейса CanDeactivate, для которого тип возвращаемого значения установлен в Observable <boolean> | Promise <boolean> | boolean, поэтому мы можем передать значение, имеющее Observable <boolean>, в качестве возвращаемого типа в методе canDeactivate

чтобы получить диалоговое окно подтверждения перед тем, как покинуть определенный маршрут, используя диалоговое окно ngx-bootstrap. У меня есть глобальная переменная под названием «canNavigate» с помощью службы. Эта переменная будет содержать логическое значение, если оно истинно или ложно, чтобы увидеть, возможна ли навигация. Это значение изначально истинно, но если я сделаю изменение в своем компоненте, я сделаю его ложным, поэтому 'canNavigate' будет ложным. Если значение равно false, я открою диалоговое окно, и если пользователь отменит изменения, он пойдет по желаемому маршруту, также приняв queryParams, иначе он не будет маршрутизировать.

@Injectable()
export class AddItemsAuthenticate implements CanDeactivate<AddUniformItemComponent> {

  bsModalRef: BsModalRef;
  constructor(private router: Router,
              private dataHelper: DataHelperService,
              private modalService: BsModalService) {
  }

  canDeactivate(component: AddUniformItemComponent,
                route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot,
                nextState?: RouterStateSnapshot): boolean {
    if (this.dataHelper.canNavigate === false ) {
      this.bsModalRef = this.modalService.show(ConfirmDialogComponent);
      this.bsModalRef.content.title = 'Discard Changes';
      this.bsModalRef.content.description = `You have unsaved changes. Do you want to leave this page and discard
                                            your changes or stay on this page?`;

      this.modalService.onHidden.subscribe(
        result => {
          try {
            if (this.bsModalRef && this.bsModalRef.content.confirmation) {
              this.dataHelper.canNavigate = true;
              this.dataHelper.reset();;
              const queryParams = nextState.root.queryParams;
              this.router.navigate([nextState.url.split('?')[0]],
                {
                  queryParams
                });
            }
          }catch (exception) {
            // console.log(exception);
          }
        }, error => console.log(error));
    }

    return this.dataHelper.canNavigate;

  }
}

Ваш ответ на вопрос