-
Notifications
You must be signed in to change notification settings - Fork 25k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(router): change location when navigating
- Loading branch information
Showing
11 changed files
with
589 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
modules/angular2/src/alt_router/location/browser_platform_location.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import {Injectable} from 'angular2/src/core/di/decorators'; | ||
import {UrlChangeListener, PlatformLocation} from './platform_location'; | ||
import {History, Location} from 'angular2/src/facade/browser'; | ||
import {DOM} from 'angular2/src/platform/dom/dom_adapter'; | ||
|
||
/** | ||
* `PlatformLocation` encapsulates all of the direct calls to platform APIs. | ||
* This class should not be used directly by an application developer. Instead, use | ||
* {@link Location}. | ||
*/ | ||
@Injectable() | ||
export class BrowserPlatformLocation extends PlatformLocation { | ||
private _location: Location; | ||
private _history: History; | ||
|
||
constructor() { | ||
super(); | ||
this._init(); | ||
} | ||
|
||
// This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it | ||
/** @internal */ | ||
_init() { | ||
this._location = DOM.getLocation(); | ||
this._history = DOM.getHistory(); | ||
} | ||
|
||
/** @internal */ | ||
get location(): Location { return this._location; } | ||
|
||
getBaseHrefFromDOM(): string { return DOM.getBaseHref(); } | ||
|
||
onPopState(fn: UrlChangeListener): void { | ||
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false); | ||
} | ||
|
||
onHashChange(fn: UrlChangeListener): void { | ||
DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false); | ||
} | ||
|
||
get pathname(): string { return this._location.pathname; } | ||
get search(): string { return this._location.search; } | ||
get hash(): string { return this._location.hash; } | ||
set pathname(newPath: string) { this._location.pathname = newPath; } | ||
|
||
pushState(state: any, title: string, url: string): void { | ||
this._history.pushState(state, title, url); | ||
} | ||
|
||
replaceState(state: any, title: string, url: string): void { | ||
this._history.replaceState(state, title, url); | ||
} | ||
|
||
forward(): void { this._history.forward(); } | ||
|
||
back(): void { this._history.back(); } | ||
} |
101 changes: 101 additions & 0 deletions
101
modules/angular2/src/alt_router/location/hash_location_strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import {Injectable, Inject, Optional} from 'angular2/core'; | ||
import {LocationStrategy, APP_BASE_HREF} from './location_strategy'; | ||
import {Location} from './location'; | ||
import {UrlChangeListener, PlatformLocation} from './platform_location'; | ||
import {isPresent} from 'angular2/src/facade/lang'; | ||
|
||
/** | ||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the | ||
* {@link Location} service to represent its state in the | ||
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) | ||
* of the browser's URL. | ||
* | ||
* For instance, if you call `location.go('/foo')`, the browser's URL will become | ||
* `example.com#/foo`. | ||
* | ||
* ### Example | ||
* | ||
* ``` | ||
* import {Component, provide} from 'angular2/core'; | ||
* import { | ||
* Location, | ||
* LocationStrategy, | ||
* HashLocationStrategy | ||
* } from 'angular2/platform/common'; | ||
* import { | ||
* ROUTER_DIRECTIVES, | ||
* ROUTER_PROVIDERS, | ||
* RouteConfig | ||
* } from 'angular2/router'; | ||
* | ||
* @Component({directives: [ROUTER_DIRECTIVES]}) | ||
* @RouteConfig([ | ||
* {...}, | ||
* ]) | ||
* class AppCmp { | ||
* constructor(location: Location) { | ||
* location.go('/foo'); | ||
* } | ||
* } | ||
* | ||
* bootstrap(AppCmp, [ | ||
* ROUTER_PROVIDERS, | ||
* provide(LocationStrategy, {useClass: HashLocationStrategy}) | ||
* ]); | ||
* ``` | ||
*/ | ||
@Injectable() | ||
export class HashLocationStrategy extends LocationStrategy { | ||
private _baseHref: string = ''; | ||
constructor(private _platformLocation: PlatformLocation, | ||
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string) { | ||
super(); | ||
if (isPresent(_baseHref)) { | ||
this._baseHref = _baseHref; | ||
} | ||
} | ||
|
||
onPopState(fn: UrlChangeListener): void { | ||
this._platformLocation.onPopState(fn); | ||
this._platformLocation.onHashChange(fn); | ||
} | ||
|
||
getBaseHref(): string { return this._baseHref; } | ||
|
||
path(): string { | ||
// the hash value is always prefixed with a `#` | ||
// and if it is empty then it will stay empty | ||
var path = this._platformLocation.hash; | ||
if (!isPresent(path)) path = '#'; | ||
|
||
// Dart will complain if a call to substring is | ||
// executed with a position value that extends the | ||
// length of string. | ||
return (path.length > 0 ? path.substring(1) : path); | ||
} | ||
|
||
prepareExternalUrl(internal: string): string { | ||
var url = Location.joinWithSlash(this._baseHref, internal); | ||
return url.length > 0 ? ('#' + url) : url; | ||
} | ||
|
||
pushState(state: any, title: string, path: string, queryParams: string) { | ||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams)); | ||
if (url.length == 0) { | ||
url = this._platformLocation.pathname; | ||
} | ||
this._platformLocation.pushState(state, title, url); | ||
} | ||
|
||
replaceState(state: any, title: string, path: string, queryParams: string) { | ||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams)); | ||
if (url.length == 0) { | ||
url = this._platformLocation.pathname; | ||
} | ||
this._platformLocation.replaceState(state, title, url); | ||
} | ||
|
||
forward(): void { this._platformLocation.forward(); } | ||
|
||
back(): void { this._platformLocation.back(); } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; | ||
import {Injectable, Inject} from 'angular2/core'; | ||
import {LocationStrategy} from './location_strategy'; | ||
|
||
/** | ||
* `Location` is a service that applications can use to interact with a browser's URL. | ||
* Depending on which {@link LocationStrategy} is used, `Location` will either persist | ||
* to the URL's path or the URL's hash segment. | ||
* | ||
* Note: it's better to use {@link Router#navigate} service to trigger route changes. Use | ||
* `Location` only if you need to interact with or create normalized URLs outside of | ||
* routing. | ||
* | ||
* `Location` is responsible for normalizing the URL against the application's base href. | ||
* A normalized URL is absolute from the URL host, includes the application's base href, and has no | ||
* trailing slash: | ||
* - `/my/app/user/123` is normalized | ||
* - `my/app/user/123` **is not** normalized | ||
* - `/my/app/user/123/` **is not** normalized | ||
* | ||
* ### Example | ||
* | ||
* ``` | ||
* import {Component} from 'angular2/core'; | ||
* import {Location} from 'angular2/platform/common'; | ||
* import { | ||
* ROUTER_DIRECTIVES, | ||
* ROUTER_PROVIDERS, | ||
* RouteConfig | ||
* } from 'angular2/router'; | ||
* | ||
* @Component({directives: [ROUTER_DIRECTIVES]}) | ||
* @RouteConfig([ | ||
* {...}, | ||
* ]) | ||
* class AppCmp { | ||
* constructor(location: Location) { | ||
* location.go('/foo'); | ||
* } | ||
* } | ||
* | ||
* bootstrap(AppCmp, [ROUTER_PROVIDERS]); | ||
* ``` | ||
*/ | ||
@Injectable() | ||
export class Location { | ||
/** @internal */ | ||
_subject: EventEmitter<any> = new EventEmitter(); | ||
/** @internal */ | ||
_baseHref: string; | ||
|
||
constructor(public platformStrategy: LocationStrategy) { | ||
var browserBaseHref = this.platformStrategy.getBaseHref(); | ||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref)); | ||
this.platformStrategy.onPopState((ev) => { | ||
ObservableWrapper.callEmit(this._subject, {'url': this.path(), 'pop': true, 'type': ev.type}); | ||
}); | ||
} | ||
|
||
/** | ||
* Returns the normalized URL path. | ||
*/ | ||
path(): string { return this.normalize(this.platformStrategy.path()); } | ||
|
||
/** | ||
* Given a string representing a URL, returns the normalized URL path without leading or | ||
* trailing slashes | ||
*/ | ||
normalize(url: string): string { | ||
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url))); | ||
} | ||
|
||
/** | ||
* Given a string representing a URL, returns the platform-specific external URL path. | ||
* If the given URL doesn't begin with a leading slash (`'/'`), this method adds one | ||
* 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 { | ||
if (url.length > 0 && !url.startsWith('/')) { | ||
url = '/' + url; | ||
} | ||
return this.platformStrategy.prepareExternalUrl(url); | ||
} | ||
|
||
// TODO: rename this method to pushState | ||
/** | ||
* Changes the browsers URL to the normalized version of the given URL, and pushes a | ||
* new item onto the platform's history. | ||
*/ | ||
go(path: string, query: string = ''): void { | ||
this.platformStrategy.pushState(null, '', path, query); | ||
} | ||
|
||
/** | ||
* Changes the browsers URL to the normalized version of the given URL, and replaces | ||
* the top item on the platform's history stack. | ||
*/ | ||
replaceState(path: string, query: string = ''): void { | ||
this.platformStrategy.replaceState(null, '', path, query); | ||
} | ||
|
||
/** | ||
* Navigates forward in the platform's history. | ||
*/ | ||
forward(): void { this.platformStrategy.forward(); } | ||
|
||
/** | ||
* Navigates back in the platform's history. | ||
*/ | ||
back(): void { this.platformStrategy.back(); } | ||
|
||
/** | ||
* Subscribe to the platform's `popState` events. | ||
*/ | ||
subscribe(onNext: (value: any) => void, onThrow: (exception: any) => void = null, | ||
onReturn: () => void = null): Object { | ||
return ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn); | ||
} | ||
|
||
/** | ||
* Given a string of url parameters, prepend with '?' if needed, otherwise return parameters as | ||
* is. | ||
*/ | ||
public static normalizeQueryParams(params: string): string { | ||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params; | ||
} | ||
|
||
/** | ||
* Given 2 parts of a url, join them with a slash if needed. | ||
*/ | ||
public static joinWithSlash(start: string, end: string): string { | ||
if (start.length == 0) { | ||
return end; | ||
} | ||
if (end.length == 0) { | ||
return start; | ||
} | ||
var slashes = 0; | ||
if (start.endsWith('/')) { | ||
slashes++; | ||
} | ||
if (end.startsWith('/')) { | ||
slashes++; | ||
} | ||
if (slashes == 2) { | ||
return start + end.substring(1); | ||
} | ||
if (slashes == 1) { | ||
return start + end; | ||
} | ||
return start + '/' + end; | ||
} | ||
|
||
/** | ||
* If url has a trailing slash, remove it, otherwise return url as is. | ||
*/ | ||
public static stripTrailingSlash(url: string): string { | ||
if (/\/$/g.test(url)) { | ||
url = url.substring(0, url.length - 1); | ||
} | ||
return url; | ||
} | ||
} | ||
|
||
function _stripBaseHref(baseHref: string, url: string): string { | ||
if (baseHref.length > 0 && url.startsWith(baseHref)) { | ||
return url.substring(baseHref.length); | ||
} | ||
return url; | ||
} | ||
|
||
function _stripIndexHtml(url: string): string { | ||
if (/\/index.html$/g.test(url)) { | ||
// '/index.html'.length == 11 | ||
return url.substring(0, url.length - 11); | ||
} | ||
return url; | ||
} |
Oops, something went wrong.