Skip to content

Instantly share code, notes, and snippets.

@iffa
Last active July 11, 2019 11:55
Show Gist options
  • Save iffa/d8361fb00f5885a050806159cb70031d to your computer and use it in GitHub Desktop.
Save iffa/d8361fb00f5885a050806159cb70031d to your computer and use it in GitHub Desktop.
Modified RouterLinkWithHref directive for Angular, that triggers normally for new tab/window but emits an event for normal left click. For cases where you need custom logic only when staying on the current open tab. Tested with Angular 6+
import { ActivatedRoute, NavigationEnd, Router, RouterEvent, RouterLinkWithHref, UrlTree } from '@angular/router';
import {
Directive,
EventEmitter,
HostBinding,
HostListener,
Input,
isDevMode,
OnChanges,
OnDestroy,
Output
} from '@angular/core';
import { QueryParamsHandling } from '@angular/router/src/config';
import { Subscription } from 'rxjs';
import { LocationStrategy } from '@angular/common';
/**
* Modified {@link RouterLinkWithHref} directive that only triggers navigation if the result of the click event would result
* in the route being opened in a new tab or window. Simple left-clicking does nothing, so (leftClickEvent) can be used
* in case you need custom logic when staying in the existing tab.
*
* Add this directive to a module's declarations to use it in components.
*
* Derived from: https://github.com/angular/angular/blob/8.0.0/packages/router/src/directives/router_link.ts (tag 8.0.0)
*/
@Directive({
selector: 'a[appNewTabRouterLink],area[appNewTabRouterLink]'
})
export class NewTabRouterLinkDirective implements OnChanges, OnDestroy {
@HostBinding('attr.target') @Input() target !: string;
@Input() queryParams: { [k: string]: any };
@Input() fragment: string;
@Input() queryParamsHandling: QueryParamsHandling;
@Input() preserveFragment: boolean;
@Input() skipLocationChange: boolean;
@Input() replaceUrl: boolean;
@Input() state?: { [k: string]: any };
private commands: any[] = [];
private subscription: Subscription;
private preserve: boolean;
// the url displayed on the anchor element
@HostBinding() href: string;
// called when it is just a left click
@Output() leftClickEvent = new EventEmitter();
constructor(
private router: Router, private route: ActivatedRoute,
private locationStrategy: LocationStrategy) {
this.subscription = router.events.subscribe((s: RouterEvent) => {
if (s instanceof NavigationEnd) {
this.updateTargetUrlAndHref();
}
});
}
@Input()
set appNewTabRouterLink(commands: any[] | string) {
if (commands != null) {
this.commands = Array.isArray(commands) ? commands : [commands];
} else {
this.commands = [];
}
}
@Input()
set preserveQueryParams(value: boolean) {
if (isDevMode() && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
this.preserve = value;
}
ngOnChanges(changes: {}): any {
this.updateTargetUrlAndHref();
}
ngOnDestroy(): any {
this.subscription.unsubscribe();
}
@HostListener('click', ['$event.button', '$event.ctrlKey', '$event.metaKey', '$event.shiftKey'])
onClick(button: number, ctrlKey: boolean, metaKey: boolean, shiftKey: boolean): boolean {
if (button !== 0 || ctrlKey || metaKey || shiftKey) {
return true;
} else if (typeof this.target === 'string' && this.target !== '_self') {
return true;
} else {
// Dispatch event if left click, passing event details for consistency
this.leftClickEvent.emit({ button, ctrlKey, metaKey, shiftKey });
return false;
}
}
private updateTargetUrlAndHref(): void {
this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));
}
get urlTree(): UrlTree {
return this.router.createUrlTree(this.commands, {
relativeTo: this.route,
queryParams: this.queryParams,
fragment: this.fragment,
preserveQueryParams: attrBoolValue(this.preserve),
queryParamsHandling: this.queryParamsHandling,
preserveFragment: attrBoolValue(this.preserveFragment),
});
}
}
function attrBoolValue(s: any): boolean {
return s === '' || !!s;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment