Je souhaite ajouter une boîte de dialogue d'alerte avant que l'utilisateur ne clique sur le lien <a href="...">
.
Il existe 2 types de lien <a>
<a routerLink="/path/to/dest">
<a href="http://www.somewhere.com" target="_blank">
Je souhaite afficher un message d'alerte lorsque l'utilisateur tente de sortir du domaine Angular
Je veux appliquer à tous les <a>
clic événement (genre comme pré-hook)
Un moyen d'y parvenir?
Je le réalise en créant un composant pour <a>
, un composant de dialogue de confirmation et un service pour le dialogue.
J'utilise 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);
}
}
le fichier 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>
Une fois le composant créé, je souhaite faciliter l'appel depuis n'importe où. Créez-lui un service.
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() {
}
}
<a>
personnaliséPour faciliter son utilisation dans .html fichier, je crée un composant pour celui-ci.
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();
});
}
}
Le confirm_link
s'applique uniquement à l'ouverture d'un nouvel onglet. Sans la valeur, cela déclenchera simplement le résultat de l'abonnement à la boîte de dialogue.
Et le fichier html est très simple
<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>
Pour les liens vers d'autres vues de votre application Angular, vous pouvez implémenter un CanDeactivate route guard . Vous trouverez un exemple dansthis stackblitz, pour la page "Home".
Les liens qui naviguent en dehors de l'application doivent déclencher le gestionnaire d'événements lié à window:beforeunload
(affiché dans HomeViewComponent ci-dessous). Cependant, son comportement semble être différent dans Firefox (une boîte de confirmation est affichée) et dans Chrome (aucune boîte de confirmation ne s'affiche). Cet événement ne peut pas être testé avec stackblitz, autant que je sache.
Dans app.module:
...
import { AppRoutingModule } from './app.routing.module';
import { DeactivateGuard } from './views/home/deactivate-guard';
@NgModule({
imports: [
AppRoutingModule,
...
],
providers: [
DeactivateGuard
],
...
})
Dans 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,
],
...
})
À la maison/désactiver-garder:
import { CanDeactivate } from '@angular/router';
import { HomeViewComponent } from './home.component';
export class DeactivateGuard implements CanDeactivate<HomeViewComponent> {
canDeactivate(component: HomeViewComponent) {
return component.canDeactivate();
}
}
Dans 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?");
}
...
}
donc Angular fournit canActivate pour vérifier si vous souhaitez activer la route ou non en fonction de certaines conditions. Vous pouvez
const routes: Routes = [
{path: '/some-path', canActivate:[AuthGuard]}
];
Votre service 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;
}
}
Dans le canActivate, vous pouvez afficher un modèle générique pour lui demander s’il souhaite ou non acheminer vers une URL, et sur cette base, vous pouvez choisir le lien qui peut l’avoir ou non. Vous pouvez même écrire une logique pour routage si cela vient de balise d'ancrage ou quoi que ce soit d'autre.
Vous pouvez implémenter Route Guard qui vérifie votre condition, puis décider de rediriger ou non vers une URL cliquée, en fonction de votre choix.
Si vous suivez CLI angulaire, vous pouvez simplement installer Route Guard en lançant:
ng g guard my-new-guard
Importez le fichier guard dans app.module.ts et ajoutez-le au tableau de fournisseurs. Dans le fichier de routage, ajoutez route guard aux chemins sur lesquels vous souhaitez vérifier la condition. Comme :
const appRoutes: Routes = [
{path: '/your-path', canActivate: [route-guard]}
];
Dans votre fichier route-guard, vous pouvez implémenter votre logique de la manière suivante:
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
}
}
}
Dans le fichier .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
});
}
}
Essaye ça
En html
<a role="button" (click)="yourfunc()">
Dans tes ts
yourfunc(){
alert('navigate')
window.location.href='http://www.somewhere.com';
// your code to navigate
}