From 7703ba2387134e297c324cf2de3baf60e62e0aa4 Mon Sep 17 00:00:00 2001 From: Brandon Roberts Date: Thu, 31 Mar 2016 22:38:17 -0500 Subject: [PATCH] feat(directives): Added linkTo directive --- lib/index.ts | 5 +++- lib/link-to.ts | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/location.ts | 8 ++++-- 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 lib/link-to.ts diff --git a/lib/index.ts b/lib/index.ts index d1015a5..a4f4dbf 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -12,6 +12,7 @@ import { GUARD_PROVIDERS } from './guard'; import { MATCH_ROUTE_PROVIDERS } from './match-route'; import { COMPONENT_RENDERER_PROVIDERS } from './component-renderer'; import { QUERY_PARAMS_PROVIDERS } from './query-params'; +import { LINK_TO_PROVIDERS } from './link-to'; export function provideRouter(routes: Routes) { return [ @@ -25,7 +26,8 @@ export function provideRouter(routes: Routes) { GUARD_PROVIDERS, MATCH_ROUTE_PROVIDERS, COMPONENT_RENDERER_PROVIDERS, - QUERY_PARAMS_PROVIDERS + QUERY_PARAMS_PROVIDERS, + LINK_TO_PROVIDERS ]; } @@ -39,3 +41,4 @@ export { usePreRenderMiddleware, usePostRenderMiddleware, RenderInstruction } fr export { Routes, Route, IndexRoute } from './route'; export { useTraversalMiddleware } from './match-route'; export { QueryParams } from './query-params'; +export { LinkTo } from './link-to'; diff --git a/lib/link-to.ts b/lib/link-to.ts new file mode 100644 index 0000000..4ac54d1 --- /dev/null +++ b/lib/link-to.ts @@ -0,0 +1,73 @@ +import { + Directive, + HostBinding, + HostListener, + Input, + PLATFORM_DIRECTIVES, + provide, + Provider +} from 'angular2/core'; +import { Location } from './location'; + +/** + * The LinkTo directive links to routes in your app + * + * Links are pushed to the `Location` service to trigger a route change. + * Query params can be represented as an object or a string of names/values + * + * Home Page + * Page 1 + */ +@Directive({ selector: '[linkTo]' }) +export class LinkTo { + @Input() target: string; + @HostBinding('attr.href') linkHref; + + @Input() set linkTo(href: string){ + this._href = href; + this._updateHref(); + } + + @Input() set queryParams(params: string | Object) { + this._query = params; + this._updateHref(); + } + + private _href: string; + private _query: string | Object; + + constructor(private _location: Location) {} + + /** + * Handles click events on the associated link + * Prevents default action for non-combination click events without a target + */ + @HostListener('click', ['$event']) + onClick(event) { + if (!this._comboClick(event) && !this.target) { + this._location.go(this._href, this._query); + + event.preventDefault(); + } + } + + private _updateHref() { + this.linkHref = this._location.prepareExternalUrl(this._href, this._query); + } + + /** + * Determines whether the click event happened with a combination of other keys + */ + private _comboClick(event) { + let buttonEvent = event.which || event.button; + + return (buttonEvent > 1 || event.ctrlKey || event.metaKey || event.shiftKey); + } +} + +export const LINK_TO_PROVIDERS = [ + provide(PLATFORM_DIRECTIVES, { + multi: true, + useValue: [ LinkTo ] + }) +]; diff --git a/lib/location.ts b/lib/location.ts index f8fff4f..3adc5be 100644 --- a/lib/location.ts +++ b/lib/location.ts @@ -95,11 +95,11 @@ export class Location extends ReplaySubject{ * before normalizing. This method will also add a hash if `HashLocationStrategy` is * used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. */ - prepareExternalUrl(url: string): string { + prepareExternalUrl(url: string, query: any = ''): string { if (url.length > 0 && !url.startsWith('/')) { url = '/' + url; } - return this.platformStrategy.prepareExternalUrl(url); + return this.platformStrategy.prepareExternalUrl(url + normalizeQueryParams(normalizeQuery(query))); } /** @@ -161,6 +161,10 @@ function normalizeQuery(query: any) { return typeof query === 'string' ? query : stringifyQueryParams(query); } +function normalizeQueryParams(params: string): string { + return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params; +} + export const LOCATION_PROVIDERS = [ provide(Location, { useClass: Location }), provide(PlatformLocation, { useClass: BrowserPlatformLocation })