【问题标题】:Angular 5 add event before the route changeAngular 5在路线更改之前添加事件
【发布时间】:2017-12-27 04:21:11
【问题描述】:

我想在用户点击<a href="..."> 链接之前添加一个警告对话框。

<a>链接有两种类型

  1. 在 Angular 范围内重定向 <a routerLink="/path/to/dest">
  2. 在 Angular 应用程序之外重定向 <a href="http://www.somewhere.com" target="_blank">

当用户尝试超出 Angular 范围时,我希望能够显示一个警告框

我想申请所有<a>点击事件(有点像pre-hook)

有什么方法可以做到这一点?

【问题讨论】:

  • 您想在点击前还是点击后显示警报?
  • @ArunKumaresh 我想在用户点击时显示,但在导航出去之前显示
  • @JsLim 这是否像广告一样作为您应用程序的一部分,您想在用户单击此链接之前创建一个模式(弹出窗口)吗?
  • 您是否考虑过实施CanDeactivate route guard
  • @harold_mean2 不是广告部分,只是导航到第 3 方网站。你想在用户点击链接后创建一个模式,但在它导航出去之前

标签: angular typescript intercept


【解决方案1】:

对于指向您的 Angular 应用程序的其他视图的链接,您可以实现 CanDeactivate route guard。您将在 this stackblitz 中找到“主页”页面的示例。

在应用程序外部导航的链接应触发绑定到window:beforeunload 的事件处理程序(如下面的 HomeViewComponent 所示)。但是,它在 Firefox(显示确认框)和 Chrome(未显示确认框)中的行为似乎有所不同。据我所知,该事件无法使用 stackblitz 进行测试。


在 app.module 中:

...
import { AppRoutingModule } from './app.routing.module';
import { DeactivateGuard } from './views/home/deactivate-guard';

@NgModule({
  imports: [ 
    AppRoutingModule, 
    ... 
  ],
  providers: [
    DeactivateGuard
  ],
  ...
})

在 app.routing.module 中:

...
import { RouterModule } from '@angular/router';
import { DeactivateGuard } from './views/home/deactivate-guard';

@NgModule({
  imports: [
    RouterModule.forRoot([
      ...
      {
        path: 'home',
        component: HomeViewComponent,
        canDeactivate: [DeactivateGuard]
      },
      ...
    ])
  ],
  exports: [
    RouterModule,
  ],
  ... 
})

在家/停用保护:

import { CanDeactivate } from '@angular/router';
import { HomeViewComponent } from './home.component';

export class DeactivateGuard implements CanDeactivate<HomeViewComponent> {

  canDeactivate(component: HomeViewComponent) {
    return component.canDeactivate();
  }
}

在 home.component 中:

import { Component, HostListener } from '@angular/core';
...

@Component({
  ...
})
export class HomeViewComponent {

  @HostListener("window:beforeunload", ["$event"]) unloadHandler(event: Event) {
      event.returnValue = false;
  }

  canDeactivate() {
    return confirm("Do you want to leave?");
  }

  ...
}

【讨论】:

    【解决方案2】:

    所以 Angular 提供了 canActivate 来确定是否要根据特定条件激活路由。你可以

    const routes: Routes = [
        {path: '/some-path', canActivate:[AuthGuard]}
    ];
    

    您的 canActivate 服务

    import { Injectable } from '@angular/core';
    import { CanActivate, CanActivateChild } from '@angular/router';
    
    @Injectable()
    export class AuthGuard implements CanActivate, CanActivateChild {
    
      canActivate() {
        //ask if he really wants to route.
        console.log('i am checking to see if you are logged ')
        return true;
      }
    
      canActivateChild() {
        console.log('checking child route access');
        return true;
      }
    
    }
    

    在 canActivate 中,您可以显示一个通用模型来询问他是否要路由到 URL,并且基于此您可以控制哪个链接可以拥有它,哪个不可以。 您甚至可以为所有路由编写逻辑,无论它来自锚标记还是其他任何东西。

    【讨论】:

      【解决方案3】:

      您可以实施路由保护,检查您的情况,然后根据您的选择决定是否重定向到点击的 url。

      如果您使用 angular cli,那么您可以通过运行简单地安装路由保护:

      ng g guard my-new-guard
      

      在 app.module.ts 中导入保护文件并将其添加到 providers 数组中。 在路由文件中,将路由保护添加到要检查条件的路径。喜欢:

      const appRoutes: Routes = [
          {path: '/your-path', canActivate: [route-guard]}
      ];
      

      在您的路由保护文件中,您可以像这样实现您的逻辑:

      import { Injectable } from '@angular/core';
      import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
      import { Observable } from 'rxjs/Observable';
      
      @Injectable()
      export class AuthGuardGuard implements CanActivate {
          canActivate(
              next: ActivatedRouteSnapshot,
              state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
      
                  if(!state.url.startsWith('/')){
                     // I have check here for starting single slash for all your angular local routes. You can also check for http or https whichever you want according to your need
                     // here you can trigger your modal pop-up on its 'OK' button return true to redirect to the url
                     return true;   // or return false on 'Cancel' button of modal pop-up for cancelling route if condition doesn't fullfill
                  }
          }
      }
      

      【讨论】:

        【解决方案4】:

        试试这个

        在html中

        <a role="button" (click)="yourfunc()">
        

        在你的 ts 中

        yourfunc(){
        
           alert('navigate')
        
          window.location.href='http://www.somewhere.com';
        
          // your code to navigate
        
          }
        

        【讨论】:

        • 谢谢,但这仅适用于 1 个链接,我想在 all 链接导航出去之前拦截它
        【解决方案5】:

        在 .ts 文件中

        ngAfterViewInit() {
            var aElements = this._elementRef.nativeElement.querySelectorAll('a');
            var aElementsLen = aElements.length;
            console.log('aElements==========>', aElements);
            for(let i=0; i< aElementsLen; i++){
                console.log('aElements[i]==========>', aElements[i]);
                aElements[i].addEventListener('click',  function(e){
                    e.preventDefault();
                    //return true; // If Redirect inside of Angular app
                    return false; // Redirect outside of Angular app and show popup
                });
            }
        }
        

        Plnkr link

        【讨论】:

          【解决方案6】:

          我通过为&lt;a&gt;创建组件、确认对话框组件和对话框服务来实现它

          确认对话框

          我正在使用Angular Material

          import { Component, Inject, Output, EventEmitter } from '@angular/core';
          import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
          
          @Component({
            selector: 'confirm-dialog',
            templateUrl: './confirm-dialog.component.html',
          })
          export class ConfirmDialogComponent {
          
            constructor(
              public translate:TranslateService,
              public dialogRef: MatDialogRef<ConfirmDialogComponent>,
              @Inject(MAT_DIALOG_DATA) public data: any
              ) {
            }
            onClick(result): void {
              this.dialogRef.close(result);
            }
          
          }
          

          html 文件

          <h1 mat-dialog-title>{{data.title}}</h1>
          <div mat-dialog-content>
              <h4 class="card-title">{{ data.message }}</h4>
          </div>
          <div mat-dialog-actions class="pull-right">
              <a *ngIf="data.confirm_link" class="btn btn-primary" mat-button tabindex="-1" href="{{ data.confirm_link }}" target="_blank" (click)="onClick(true)">{{ data.confirm_button }}</a>
              <button *ngIf="!data.confirm_link" class="btn btn-primary" mat-button tabindex="-1" (click)="onClick(true)"> {{ data.confirm_button }} </button>
              <button class="btn btn-info" mat-button tabindex="-1" (click)="onClick(false)">Cancel</button>
          </div>
          

          服务

          创建组件后,我想让它在任何地方都能轻松调用,所以为它创建一个服务

          import { Injectable, OnDestroy} from "@angular/core";
          import { Subject } from 'rxjs/Subject';
          import { MatDialog } from '@angular/material';
          import { ConfirmDialogComponent } from 'path/to/confirm-dialog/confirm-dialog.component';
          import * as _ from 'lodash';
          
          @Injectable()
          export class ConfirmService implements OnDestroy{
              private subject = new Subject<any>();
              private message = 1;
              info: any;
              constructor(private dialog: MatDialog){
              }
              show(data: any){
                  let dialogRef = this.dialog.open(ConfirmDialogComponent, {
                    width: '500px',
                    data: data,
                  });
          
                  dialogRef.afterClosed().subscribe(result => {
                    this.subject.next(result);
                  });
                  return this.subject;
              }
              ngOnDestroy() {
          
              }
          }
          

          自定义&lt;a&gt; 元素

          为了方便在.html文件中使用,我为它创建了一个组件

          import { Component, OnInit, Input } from '@angular/core';
          import { ConfirmService } from 'path/to/service/confirm.service';
          
          @Component({
            selector: 'a-external',
            templateUrl: './a-external.component.html',
          })
          export class AExternalComponent implements OnInit {
            @Input('href') href: string;
            @Input('class') classes: string;
            @Input('content') content: string;
          
            constructor(
              private confirmService:ConfirmService,
            ) { }
          
            ngOnInit() {
            }
          
            onAClick() {
              var dialog = this.confirmService.show({
                'title': 'Warning',
                'message': 'This will open a new tab',
                'confirm_button': 'open',
                'confirm_link': this.href, // if pass in the uri, will open in new tab
              });
              var subscription = dialog.subscribe((result) => {
                // if the result is true, means Confirm button is clicked
                // if the result is false, means Cancel button is clicked
                subscription.unsubscribe();
              });
            }
          }
          

          confirm_link 仅适用于打开新标签。没有该值,只会触发对话订阅结果。

          而且html文件很简单

          <a href="javascript:" class="{{ classes }}" (click)="onAClick()">{{ content }}</a>
          

          使用它

          <a-external [href]="http://www.foobar.com" [class]="'btn btn-info'" [content]="'The content inside a element'"></a-external>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-03-21
            • 2019-07-31
            相关资源
            最近更新 更多