-
Notifications
You must be signed in to change notification settings - Fork 24.8k
/
location_strategy.ts
171 lines (152 loc) Β· 5.66 KB
/
location_strategy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, inject, Injectable, InjectionToken, OnDestroy, Optional} from '@angular/core';
import {DOCUMENT} from '../dom_tokens';
import {LocationChangeListener, PlatformLocation} from './platform_location';
import {joinWithSlash, normalizeQueryParams} from './util';
/**
* Enables the `Location` service to read route state from the browser's URL.
* Angular provides two strategies:
* `HashLocationStrategy` and `PathLocationStrategy`.
*
* Applications should use the `Router` or `Location` services to
* interact with application route state.
*
* For instance, `HashLocationStrategy` produces URLs like
* <code class="no-auto-link">http://example.com#/foo</code>,
* and `PathLocationStrategy` produces
* <code class="no-auto-link">http://example.com/foo</code> as an equivalent URL.
*
* See these two classes for more.
*
* @publicApi
*/
@Injectable({providedIn: 'root', useFactory: () => inject(PathLocationStrategy)})
export abstract class LocationStrategy {
abstract path(includeHash?: boolean): string;
abstract prepareExternalUrl(internal: string): string;
abstract getState(): unknown;
abstract pushState(state: any, title: string, url: string, queryParams: string): void;
abstract replaceState(state: any, title: string, url: string, queryParams: string): void;
abstract forward(): void;
abstract back(): void;
historyGo?(relativePosition: number): void {
throw new Error('Not implemented');
}
abstract onPopState(fn: LocationChangeListener): void;
abstract getBaseHref(): string;
}
/**
* A predefined [DI token](guide/glossary#di-token) for the base href
* to be used with the `PathLocationStrategy`.
* The base href is the URL prefix that should be preserved when generating
* and recognizing URLs.
*
* @usageNotes
*
* The following example shows how to use this token to configure the root app injector
* with a base href value, so that the DI framework can supply the dependency anywhere in the app.
*
* ```typescript
* import {Component, NgModule} from '@angular/core';
* import {APP_BASE_HREF} from '@angular/common';
*
* @NgModule({
* providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
* })
* class AppModule {}
* ```
*
* @publicApi
*/
export const APP_BASE_HREF = new InjectionToken<string>('appBaseHref');
/**
* @description
* A {@link LocationStrategy} used to configure the {@link Location} service to
* represent its state in the
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
* browser's URL.
*
* If you're using `PathLocationStrategy`, you may provide a {@link APP_BASE_HREF}
* or add a `<base href>` element to the document to override the default.
*
* For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`. To ensure all relative URIs resolve correctly,
* the `<base href>` and/or `APP_BASE_HREF` should end with a `/`.
*
* Similarly, if you add `<base href='/my/app/'/>` to the document and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* Note that when using `PathLocationStrategy`, neither the query nor
* the fragment in the `<base href>` will be preserved, as outlined
* by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2).
*
* @usageNotes
*
* ### Example
*
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
*
* @publicApi
*/
@Injectable({providedIn: 'root'})
export class PathLocationStrategy extends LocationStrategy implements OnDestroy {
private _baseHref: string;
private _removeListenerFns: (() => void)[] = [];
constructor(
private _platformLocation: PlatformLocation,
@Optional() @Inject(APP_BASE_HREF) href?: string) {
super();
this._baseHref = href ?? this._platformLocation.getBaseHrefFromDOM() ??
inject(DOCUMENT).location?.origin ?? '';
}
/** @nodoc */
ngOnDestroy(): void {
while (this._removeListenerFns.length) {
this._removeListenerFns.pop()!();
}
}
override onPopState(fn: LocationChangeListener): void {
this._removeListenerFns.push(
this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn));
}
override getBaseHref(): string {
return this._baseHref;
}
override prepareExternalUrl(internal: string): string {
return joinWithSlash(this._baseHref, internal);
}
override path(includeHash: boolean = false): string {
const pathname =
this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search);
const hash = this._platformLocation.hash;
return hash && includeHash ? `${pathname}${hash}` : pathname;
}
override pushState(state: any, title: string, url: string, queryParams: string) {
const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
this._platformLocation.pushState(state, title, externalUrl);
}
override replaceState(state: any, title: string, url: string, queryParams: string) {
const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
this._platformLocation.replaceState(state, title, externalUrl);
}
override forward(): void {
this._platformLocation.forward();
}
override back(): void {
this._platformLocation.back();
}
override getState(): unknown {
return this._platformLocation.getState();
}
override historyGo(relativePosition: number = 0): void {
this._platformLocation.historyGo?.(relativePosition);
}
}