代码之家  ›  专栏  ›  技术社区  ›  Francesco Borzi

canDeactivate Guard服务中的Angular use modal对话框,用于未提交的更改(表单脏)

  •  18
  • Francesco Borzi  · 技术社区  · 7 年前

    在Angular 4应用程序中,我有一些具有表单的组件,如下所示:

    export class MyComponent implements OnInit, FormComponent {
    
      form: FormGroup;
    
      ngOnInit() {
        this.form = new FormGroup({...});
      }
    

    他们使用保护服务来防止未提交的更改丢失,因此如果用户在请求确认之前尝试更改路由:

    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 Modal

    我如何使用模态来实现相同的结果?

    8 回复  |  直到 7 年前
        1
  •  32
  •   Virendra Singh Rathore    6 年前

    我用 ngx-bootstrap Modals RxJs Subjects .

    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;
      }
    }
    

    应用程序内。单元ts文件转到@NgModule部分,并将ConfirmLeaveComponent组件添加到entryComponents。

    @NgModule({
      entryComponents: [
        ConfirmLeaveComponent,
      ]
    })
    
        2
  •  3
  •   mitschmidt    7 年前

    除了ShinDarth的好解决方案之外,似乎值得一提的是,您还必须涵盖对模式的取消,因为action()方法可能不会被激发(例如,如果您允许esc按钮或在模式外单击)。在这种情况下,observable永远不会完成,如果你使用它进行路由,你的应用程序可能会卡住。

    onHide 并将其与行为主体合并在一起:

    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
        );
      }
    

    在我的例子中,我映射了提到的 可以观察到错误,因为在我的案例中,解雇被视为中止( 只有

        3
  •  3
  •   Cameron Forward    5 年前

    只需扩展mitschmidt提供的关于单击外部/转义按钮的附加信息,这个canDeactivate方法可以与Francesco Borzi的代码一起工作。我只是在函数中内联添加了subscribe to onHide():

    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;
    
                this.modalService.onHide.subscribe(hide => {
                    subject.next(false);
                    return subject.asObservable();
                });
    
                return subject.asObservable();
            }
    
            return true;
        }
    
        4
  •  2
  •   Andrew Lobban    5 年前

    由于我一直在来回与阿什温,我决定张贴我的解决方案,我与角度和材料。

    这是我的 StackBlitz

        5
  •  1
  •   Dilshan Liyanage    7 年前

    这是我的实现,使用ngx引导对话框在离开某个路由之前获得一个确认对话框。在服务的帮助下,我有一个名为“canNavigate”的全局变量。如果为true或false,此变量将保留布尔值,以查看是否可以进行导航。该值最初为true,但如果我在组件中进行更改,我将使其为false,因此“canNavigate”将为false。如果为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;
    
      }
    }
    
        6
  •  1
  •   meesvandongen    4 年前

    您可以将值传递给 afterClosed

    // modal.component.html
    <mat-dialog-actions>
      <button mat-button mat-dialog-close>Cancel</button>
      <button mat-button [mat-dialog-close]="true">Leave</button>
    </mat-dialog-actions>
    
    // unsaved-changes.service.ts
    @Injectable({ providedIn: 'root' })
    export class UnsavedChangesGuardService
      implements CanDeactivate<FormComponent> {
      constructor(private _dialog: MatDialog) {}
    
      canDeactivate(component: FormComponent) {
        if (component.form.dirty) {
          const dialogRef = this._dialog.open(UnsavedChangesDialogComponent);
          return dialogRef.afterClosed();
        }
    
        return true;
      }
    }
    
        7
  •  0
  •   Or Zohar    5 年前

    我使用“角度材质”对话框实现了此解决方案:

    if (component.isDirty()) {
      const subject = new Subject<boolean>();
      const modal = this.dialog.open(ConfirmationDialogComponent, {
        panelClass: 'my-panel', width: '400px', height: '400px',
      });
    
      modal.componentInstance.subject = subject;
      return subject.asObservable()
    }
      return true;
    }
    
        8
  •  0
  •   Fateh Mohamed    3 年前

    这是一个没有主题的工作解决方案,您可以添加布尔属性

    import { Component, OnInit } from '@angular/core';
    import { BsModalRef } from 'ngx-bootstrap/modal';
    
    @Component({
      selector: 'app-leave-form-confirmation',
      templateUrl: './leave-form-confirmation.component.html',
      styleUrls: ['./leave-form-confirmation.component.scss']
    })
    export class LeaveFormConfirmationComponent implements OnInit {
      confirmed = false;
      constructor(public bsModalRef: BsModalRef) { }
    
      ngOnInit(): void {
      }
      confirm = () => {
        this.confirmed= true;
        this.bsModalRef.hide()
      }
    }
    

    这是html

    <div class="modal-header">
      <h4 class="modal-title pull-left">Confirmation</h4>
    </div>
    <div class="modal-body">
      <h2>Data will be lost, Are you sure to leave the form?</h2>
    </div>-*
    <div class="modal-footer">
      <button type="button" class="btn btn-default" (click)="confirm()">confirm</button>
      <button type="button" class="btn btn-default" (click)="bsModalRef.hide()">cancel</button>
    </div>
    

     canDeactivate(
        component: DataStatus,
        currentRoute: ActivatedRouteSnapshot,
        currentState: RouterStateSnapshot,
        nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        if(!component.isDataSaved()) {
          const modalRef = this.modalService.show(LeaveFormConfirmationComponent,  {
            backdrop: false,
            ignoreBackdropClick: true
          })
          return modalRef.onHidden.pipe(map(_ => modalRef.content.confirmed));
        }
        return of(true);
      }