在浏览器后面关闭Angular Material对话框

时间:2017-12-08 23:14:28

标签: angular angular-material

我正在使用Angular Material(5.0.0)为移动设备创建单页Web应用程序。我有一个需要显示对话框的场景。我想允许用户回击关闭对话框,因为它在移动设备上非常常见(特别是在Android上)。

当前发生这种情况时,页面会转到上一页。我需要按钮来简单地关闭对话框。

关于如何实现这一点的任何想法?

13 个答案:

答案 0 :(得分:3)

作为解决方法,您可以将CanActivateChild guard添加到根路由。如果对话框打开,警卫将取消导航,然后关闭对话框。

路由配置:

const rootRoutes: Routes = [{
    path: '',
    canActivateChild: [ CanActivateChildGuard ],
    children: [
        ...
    ]
}];

还有后卫:

export class CanActivateChildGuard implements CanActivateChild {
    constructor(
        private readonly router: Router,
        private readonly location: Location
    ) {}

    canActivateChild(route: ActivatedRouteSnapshot): boolean {
        if (this.dialog.openDialogs.length > 0) {
            // fix navigation history, see github issue for more details
            // https://github.com/angular/angular/issues/13586
            const currentUrlTree = this.router.createUrlTree([], route);
            const currentUrl = currentUrlTree.toString();
            this.location.go(currentUrl);

            this.dialog.closeAll();
            return false;
        } else {
            return true;
        }
    }
}

答案 1 :(得分:2)

此选项已可用

let dialogRef = dialog.open(DialogExample, {
  height: '400px',
  width: '600px',
  closeOnNavigation: true
});

使用路线更改事件的其他方式:

1。来自应用程序组件

  constructor(router: Router, matDialog: MatDialog) {

    // Close any opened dialog when route changes
    router.events.pipe(
      filter((event: RouterEvent) => event instanceof NavigationStart),
      tap(() => this.matDialog.closeAll())
    ).subscribe();
  }

2。来自对话框组件

@Component({
  selector: 'example-dialog',
  templateUrl: 'example-dialog.html',
})
export class ExampleDialog {

  constructor(
    public dialogRef: MatDialogRef<ExampleDialog>,
    router: Router
  ) {

    // Close dialog ref on route changes
    router.events.pipe(
      filter((event: RouterEvent) => event instanceof NavigationStart),
      tap(() => this.dialogRef.close()),
      take(1),
    ).subscribe();
  }

}

答案 2 :(得分:1)

您可以注入MatDialog服务并调用closeAll方法关闭所有已打开的对话框,如下所示

constructor(public dialog: MatDialog) {
         this.dialog.closeAll();
}

<强> LIVE DEMO

答案 3 :(得分:1)

我发现,如果您预先调用history.pushState()打开模态,则可以通过向后导航来“关闭”模态。

...
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = false;
dialogConfig.autoFocus = false;
dialogConfig.closeOnNavigation = true;

history.pushState({ foo: "bar" }, "Image", "/currentpage#");    

return this.dialog.open(PictureGalleryComponent, dialogConfig).afterClosed();
...

此外,[mat-dialog-close]="true"仍然有效,因为哈希不会损害当前网址。

还是有点hacky。

答案 4 :(得分:1)

我正在做的是利用$args = array( 'post_type' => 'resources', 'post_status' => 'publish', 'posts_per_page' => $card_count, //'cat' => $resource_type, array( 'taxonomy' => 'type', 'terms' => $resource_type, 'field' => 'name', 'operator' => 'IN', 'include_children' => true, ) ); 选项和浏览器的历史记录。

该想法基本上是在初始化对话框组件时在浏览器历史记录中复制当前状态,并将MatDialogConfig.closeOnNavigation属性设置为true,以便当用户在其浏览器上单击时,对话框关闭,但他仍然使用相同的网址(因为我们在历史记录中重复了该网址)。这可能不是最好的解决方案,但似乎工作正常。

closeOnNavigation打开对话框:

whatever.component.ts

实际的 export class WhateverComponent { constructor(dialog: MatDialog) { } openDialog() { this.dialog.open(MyDialog, { closeOnNavigation: true }).backdropClick().subscribe(e => history.back()); } }

dialog.component.ts

我将其包装在 export class MyDialog implements OnInit { constructor(public dialogRef: MatDialogRef<MyDialog>, @Inject(PLATFORM_ID) private platformId: Object) { } public ngOnInit() { if (isPlatformBrowser(this.platformId)) { history.pushState({}, document.getElementsByTagName('title')[0].innerHTML, window.location.href); } } } 中,因为我担心它会引发SSR错误。

答案 5 :(得分:1)

关于以下解决方案,我从@valeriy-katkov答案中得到了启发,得到了改进和简化。

  1. 配置对话框,使其在导航时不关闭

closeOnNavigation

this.dialog.open(Component, {
      ...
      closeOnNavigation: false
    });
  1. 在根路由上添加CanActivateChild防护。

路由配置:

const rootRoutes: Routes = [{
    path: '',
    canActivateChild: [ CanActivateChildGuard ],
    children: [
        ...
    ]
}];
  1. 如果对话框打开,则防护将取消浏览器上的导航,而将关闭对话框。

简单的警卫:

export class CanActivateChildGuard implements CanActivateChild {
    constructor(private dialog: MatDialog) {}

    canActivateChild(): boolean {
        if (this.dialog.openDialogs.length > 0) {
            this.dialog.closeAll();
            return false;
        } else {
            return true;
        }
    }
}

答案 6 :(得分:0)

您可以收听popstate,然后执行诸如关闭对话框之类的操作。

import { HostListener } from '@angular/core';
  @HostListener('window:popstate', ['$event'])
  onPopState(event) {
      this.dialog.closeAll();

}

或使用“位置”。

import {Location} from "@angular/common";

constructor(private location: Location) { }

ngOnInit() {
   this.location.subscribe(x => if back then close dialog);
}

答案 7 :(得分:0)

每次打开对话框时,请将查询参数添加到您的网址

ex: /test?dlgopen=true

关闭对话框时,从网址中删除dlgopen,其余的将由浏览器自动处理。 家有帮助

答案 8 :(得分:0)

我也在试图实现这一目标,并找到了另一种很棒的方法...

为对话框设置自己的路线

这里有一篇很好的文章对此进行了解释:https://medium.com/ngconf/routing-to-angular-material-dialogs-c3fb7231c177

基本步骤:

  • 将子路线添加到要打开对话框的路线
  • 在要打开对话框的路线的html中添加<router-outlet></router-outlet>
  • 当用户单击按钮(或等效按钮)并且应该打开对话框时,导航到该子路线
  • 构造子路线组件后,打开对话框
  • 在新组件中,处理对话框关闭,并导航到父路线

在我的情况下,效果很好。它的优点是对话框是一条路线,因此您甚至可以链接到它,并且在开发和处理对话框时,不必一直重新打开它!

答案 9 :(得分:0)

我的解决方案是使用closeOnNavigation:false打开所有对话框,然后使用此代码,甚至适用于重叠对话框

// push history state when a dialog is opened
dialogRef.afterOpened.subscribe((ref: MatDialogRef<any, any>) => {

  // when opening a dialog, push a new history entry with the dialog id
  location.go('', '', ref.id);

  ref.afterClosed().subscribe(() => {
    // when closing but the dialog is still the current state (because it has not been closed via the back button), pop a history entry
    if (location.getState() === ref.id) {
      history.go(-1);
    }
  });

});

location.subscribe((event: PopStateEvent) => {
  const frontDialog = dialogRef.openDialogs[dialogRef.openDialogs.length - 1];
  // when the user hits back, the state wont equal the front popup anymore, so close it
  // when a popup was closed manually, the state should match the underlying popup, and we wont close the current front
  if (frontDialog && location.getState() !== frontDialog.id) {
    frontDialog.close();
  }
});

答案 10 :(得分:0)

也许这种方法会帮助您。

此代码位于实际的对话框组件中,在这种情况下, PesonComponent

import { Location } from '@angular/common';


constructor(
     private location: Location,
     @Optional() @Inject(MAT_DIALOG_DATA) private person: Person,
     @Optional() private personDialogRef: MatDialogRef<PersonComponent>
) { }

现在只需订阅afterOpenedafterclosed并相应地更改URL

ngOnInit(): void {

    let currentUrl = this.location.path(); // saving current URL to update afterClosed


    this.orderInfoDialogRef.afterOpened().pipe(
      takeUntil(this.onDestroy),
      tap(() => this.location.go(currentUrl + `/${this.person._id}`)) // this will change the URL to /person/123
    ).subscribe();


    this.orderInfoDialogRef.afterClosed().pipe(
      takeUntil(this.onDestroy),
      tap(() => this.location.go(currentUrl)) // this will revert the URL back to /person
    ).subscribe();

}

}

现在,触摸 Android 等移动设备上的后退按钮,观察URL如何从/person/123更改为person,并关闭对话框。

如果您使用关闭对话框的按钮,则afterClosed也会将URL更改回/person

祝你好运!

答案 11 :(得分:0)

以下是我关闭后方按键对话框的解决方案,它在android手机和浏览器中均能正常工作

DialogComponent{
...

  ngOnInit() {
    this.handleBackKey();
  }

  handleBackKey() {
    window.history.pushState(null, "Back", window.location.href);

    this.dialog.afterClosed().subscribe((res) => {
      window.onpopstate = null;
      window.history.go(-1);
    });

    window.onpopstate = () => {
      this.dialog.close();
      window.history.pushState(null, "Back", window.location.href);
    };
  }

....
}

答案 12 :(得分:0)

我有一个类似的用例,并使用 CanDeactivate - Guard 解决了这个问题。与 CanAvtivate-Guard 相比,这样做的好处是您只需为那些实际打开 Dialog 的组件设置 Guard。在我的应用程序中,只有 2 个组件。

首先我将 closeOnNavigation 设置为 false,以便在导航开始时不会立即关闭对话框:

 const matDialogRef = this.matDialog.open(AddAbilityToHeroDialogComponent,
      { ...
        closeOnNavigation: false,
      });

第二次我实现了 Guard - 基本上我注入了 MatDialog 并检查是否有打开的对话框。如果是这样,我将中止导航并关闭所有对话框。

@Injectable({
  providedIn: 'root'
})
export class CloseModalOnBrowserBackIfNecessaryDeactivateGuard implements CanDeactivate<Component> {
  constructor(private dialog: MatDialog) {
  }

  canDeactivate(component: Component, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.dialog.openDialogs.length > 0) {
      this.dialog.closeAll();
      return false;
    } else {
      return true;
    }
  }
}

最后,我将 Guard 添加到打开对话框的路由中,如下所示:

 {path: "edit/:id", component: HeroesEditComponent, canDeactivate: [CloseModalOnBrowserBackIfNecessaryDeactivateGuard]}