Skip to content

Conversation

@atscott
Copy link
Contributor

@atscott atscott commented Jan 12, 2026

Adds dedicated LocationStrategy subclasses: NoTrailingSlashPathLocationStrategy and TrailingSlashPathLocationStrategy.

The TrailingSlashPathLocationStrategy ensures that URLs prepared for the browser always end with a slash, while NoTrailingSlashPathLocationStrategy ensures they never do. This configuration only affects the URL written to the browser history; the Location service continues to normalize paths by stripping trailing slashes when reading from the browser.

Example:

providers: [
  {provide: LocationStrategy, useClass: TrailingSlashPathLocationStrategy}
]

This approach to the trailing slash problem isolates the changes to the
existing LocationStrategy abstraction without changes to Router, as was
attempted in two other options (#66452 and #66423).

From an architectural perspective, this is the cleanest approach for several reasons:

  1. Separation of Concerns and "Router Purity": The Router's primary job is to map a URL structure to an application state (ActivatedRoutes). It shouldn't necessarily be burdened with the formatting nuances of the underlying platform unless those nuances affect the state itself. By pushing trailing slash handling to the LocationStrategy, you treat the trailing slash as a "platform serialization format" rather than a "router state" concern. This avoids the "weirdness" in feat(router): refine trailing slash handling with hybrid ephemeral/append strategy #66423 where the UrlTree (serialization format) disagrees with the ActivatedRouteSnapshot (logical state).

  2. Tree Shakability: If an application doesn't care about trailing slashes (which is the default "never" behavior), they don't pay the cost for that logic. It essentially becomes a swappable "driver" for the URL interaction.

  3. Simplicity for the Router: feat(router): add trailingSlash config option #66452 (consuming the slash as a segment) bleeds into the matching logic, potentially causing issues with child routes or wildcards effectively "eating" a segment that should be invisible. This option leaves the matching logic purely focused on meaningful path segments by continuing to strip the trailing slash on read.

  4. Consistency with Existing Patterns: Angular already uses LocationStrategy to handle Hash vs Path routing. Adding "Trailing Slash" nuances there is a natural extension of that pattern—it's just another variation of "how do we represent this logic in the browser's address bar?"

fixes #16051

@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: common Issues related to APIs in the @angular/common package labels Jan 12, 2026
@ngbot ngbot bot added this to the Backlog milestone Jan 12, 2026
@atscott atscott force-pushed the trailingSlashLocationOnly branch from 28634ce to 4c4de7d Compare January 12, 2026 20:08
@atscott atscott changed the title feat(common): replace REMOVE_TRAILING_SLASH with LocationStrategy subclasses feat(common): Add location strategies to manage trailing slash on write Jan 12, 2026
@atscott atscott force-pushed the trailingSlashLocationOnly branch from 4c4de7d to 77ac828 Compare January 12, 2026 20:15
Adds dedicated `LocationStrategy` subclasses: `NoTrailingSlashPathLocationStrategy` and `TrailingSlashPathLocationStrategy`.

The `TrailingSlashPathLocationStrategy` ensures that URLs prepared for the browser always end with a slash, while `NoTrailingSlashPathLocationStrategy` ensures they never do. This configuration only affects the URL written to the browser history; the `Location` service continues to normalize paths by stripping trailing slashes when reading from the browser.

Example:
```typescript
providers: [
  {provide: LocationStrategy, useClass: TrailingSlashPathLocationStrategy}
]
```

This approach to the trailing slash problem isolates the changes to the
existing LocationStrategy abstraction without changes to Router, as was
attempted in two other options (angular#66452 and angular#66423).

From an architectural perspective, this is the cleanest approach for several reasons:

1. Separation of Concerns and "Router Purity": The Router's primary job is to map a URL structure to an application state (ActivatedRoutes). It shouldn't necessarily be burdened with the formatting nuances of the underlying platform unless those nuances affect the state itself. By pushing trailing slash handling to the LocationStrategy, you treat the trailing slash as a "platform serialization format" rather than a "router state" concern. This avoids the "weirdness" in angular#66423 where the UrlTree (serialization format) disagrees with the ActivatedRouteSnapshot (logical state).

2. Tree Shakability: If an application doesn't care about trailing slashes (which is the default "never" behavior), they don't pay the cost for that logic. It essentially becomes a swappable "driver" for the URL interaction.

3. Simplicity for the Router: angular#66452 (consuming the slash as a segment) bleeds into the matching logic, potentially causing issues with child routes or wildcards effectively "eating" a segment that should be invisible. This option leaves the matching logic purely focused on meaningful path segments by continuing to strip the trailing slash on read.

4. Consistency with Existing Patterns: Angular already uses LocationStrategy to handle Hash vs Path routing. Adding "Trailing Slash" nuances there is a natural extension of that pattern—it's just another variation of "how do we represent this logic in the browser's address bar?"

fixes angular#16051
@atscott atscott force-pushed the trailingSlashLocationOnly branch from 77ac828 to 8b4a8d1 Compare January 12, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: common Issues related to APIs in the @angular/common package detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Routing: Preserve trailing slash

1 participant