Skip to content

Commit

Permalink
fix(router): Remove deprecated relativeLinkResolution (#47623)
Browse files Browse the repository at this point in the history
The `relativeLinkResolution` is no longer supported. The only behavior
now is the correct behavior.

BREAKING CHANGE: `relativeLinkResolution` is no longer configurable in
the Router. This option was used as a means to opt out of a bug fix.

PR Close #47623
  • Loading branch information
atscott authored and AndrewKushnir committed Oct 5, 2022
1 parent 2cdf128 commit 7b89d95
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 129 deletions.
6 changes: 1 addition & 5 deletions goldens/public-api/router/index.md
Expand Up @@ -288,14 +288,12 @@ export const enum EventType {
}

// @public
export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions {
export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions, InternalExtraOptions {
enableTracing?: boolean;
errorHandler?: ErrorHandler;
initialNavigation?: InitialNavigation;
malformedUriErrorHandler?: (error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree;
preloadingStrategy?: any;
// @deprecated
relativeLinkResolution?: 'legacy' | 'corrected';
scrollOffset?: [number, number] | (() => [number, number]);
useHash?: boolean;
}
Expand Down Expand Up @@ -653,8 +651,6 @@ export class Router {
onSameUrlNavigation: 'reload' | 'ignore';
paramsInheritanceStrategy: 'emptyOnly' | 'always';
parseUrl(url: string): UrlTree;
// @deprecated
relativeLinkResolution: 'legacy' | 'corrected';
resetConfig(config: Routes): void;
routeReuseStrategy: RouteReuseStrategy;
readonly routerState: RouterState;
Expand Down
2 changes: 2 additions & 0 deletions packages/router/src/patchable_relative_link_resolution.ts
Expand Up @@ -11,3 +11,5 @@
*/
export function assignRelativeLinkResolution(
router: {relativeLinkResolution: 'legacy'|'corrected'}): void {}

export interface InternalExtraOptions {}
11 changes: 3 additions & 8 deletions packages/router/src/router.ts
Expand Up @@ -310,10 +310,6 @@ export function assignExtraOptionsToRouter(opts: ExtraOptions, router: Router):
router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
}

if (opts.relativeLinkResolution) {
router.relativeLinkResolution = opts.relativeLinkResolution;
}

if (opts.urlUpdateStrategy) {
router.urlUpdateStrategy = opts.urlUpdateStrategy;
}
Expand Down Expand Up @@ -531,10 +527,9 @@ export class Router {
urlUpdateStrategy: 'deferred'|'eager' = 'deferred';

/**
* Enables a bug fix that corrects relative link resolution in components with empty paths.
* @see `RouterModule`
*
* @deprecated
* TODO(atscott): Remove all references to relativeLinkResolution when internal cleanup is
* finished.
* @internal
*/
relativeLinkResolution: 'legacy'|'corrected' = 'corrected';

Expand Down
40 changes: 3 additions & 37 deletions packages/router/src/router_config.ts
Expand Up @@ -8,6 +8,7 @@

import {InjectionToken} from '@angular/core';

import {InternalExtraOptions} from './patchable_relative_link_resolution';
import {UrlSerializer, UrlTree} from './url_tree';

const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
Expand Down Expand Up @@ -172,7 +173,8 @@ export interface InMemoryScrollingOptions {
*
* @publicApi
*/
export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions {
export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOptions,
InternalExtraOptions {
/**
* When true, log all internal navigation events to the console.
* Use for debugging.
Expand Down Expand Up @@ -234,42 +236,6 @@ export interface ExtraOptions extends InMemoryScrollingOptions, RouterConfigOpti
* */
malformedUriErrorHandler?:
(error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree;

/**
* Enables a bug fix that corrects relative link resolution in components with empty paths.
* Example:
*
* ```
* const routes = [
* {
* path: '',
* component: ContainerComponent,
* children: [
* { path: 'a', component: AComponent },
* { path: 'b', component: BComponent },
* ]
* }
* ];
* ```
*
* From the `ContainerComponent`, you should be able to navigate to `AComponent` using
* the following `routerLink`, but it will not work if `relativeLinkResolution` is set
* to `'legacy'`:
*
* `<a [routerLink]="['./a']">Link to A</a>`
*
* However, this will work:
*
* `<a [routerLink]="['../a']">Link to A</a>`
*
* In other words, you're required to use `../` rather than `./` when the relative link
* resolution is set to `'legacy'`.
*
* The default in v11 is `corrected`.
*
* @deprecated
*/
relativeLinkResolution?: 'legacy'|'corrected';
}

/**
Expand Down
111 changes: 32 additions & 79 deletions packages/router/test/integration.spec.ts
Expand Up @@ -83,45 +83,22 @@ describe('Integration', () => {
]);
})));

describe('relativeLinkResolution', () => {
beforeEach(inject([Router], (router: Router) => {
router.resetConfig([{
path: 'foo',
children: [{path: 'bar', children: [{path: '', component: RelativeLinkCmp}]}]
}]);
}));

it('should not ignore empty paths in legacy mode',
fakeAsync(inject([Router], (router: Router) => {
const warnSpy = spyOn(console, 'warn');
router.relativeLinkResolution = 'legacy';

const fixture = createRoot(router, RootCmp);

router.navigateByUrl('/foo/bar');
advance(fixture);

const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/foo/bar/simple');
expect(warnSpy.calls.first().args[0])
.toContain('/foo/bar/simple will change to /foo/simple');
expect(warnSpy.calls.first().args[0])
.toContain('relativeLinkResolution: \'legacy\' is deprecated');
})));

it('should ignore empty paths in corrected mode',
fakeAsync(inject([Router], (router: Router) => {
router.relativeLinkResolution = 'corrected';
it('should ignore empty paths in relative links',
fakeAsync(inject([Router], (router: Router) => {
router.resetConfig([{
path: 'foo',
children: [{path: 'bar', children: [{path: '', component: RelativeLinkCmp}]}]
}]);

const fixture = createRoot(router, RootCmp);
const fixture = createRoot(router, RootCmp);

router.navigateByUrl('/foo/bar');
advance(fixture);
router.navigateByUrl('/foo/bar');
advance(fixture);

const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/foo/simple');
})));
});
const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/foo/simple');
})));

it('should set the restoredState to null when executing imperative navigations',
fakeAsync(inject([Router], (router: Router) => {
Expand Down Expand Up @@ -6474,55 +6451,31 @@ describe('Integration', () => {
expect(relativeLinkCmp.buttonLink.urlTree.toString()).toEqual('/root/childRoot');
}));

describe('relativeLinkResolution', () => {
@Component({selector: 'link-cmp', template: `<a [routerLink]="['../simple']">link</a>`})
class RelativeLinkCmp {
}

@NgModule({
declarations: [RelativeLinkCmp],
imports: [RouterModule.forChild([
{path: 'foo/bar', children: [{path: '', component: RelativeLinkCmp}]},
])]
})
class LazyLoadedModule {
}

it('should not ignore empty path when in legacy mode',
fakeAsync(inject([Router], (router: Router) => {
const warnSpy = spyOn(console, 'warn');
router.relativeLinkResolution = 'legacy';

const fixture = createRoot(router, RootCmp);

router.resetConfig([{path: 'lazy', loadChildren: () => LazyLoadedModule}]);

router.navigateByUrl('/lazy/foo/bar');
advance(fixture);

const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/lazy/foo/bar/simple');
expect(warnSpy.calls.first().args[0])
.toContain('/lazy/foo/bar/simple will change to /lazy/foo/simple');
expect(warnSpy.calls.first().args[0])
.toContain('relativeLinkResolution: \'legacy\' is deprecated');
})));
it('should ignore empty path for relative links',
fakeAsync(inject([Router], (router: Router) => {
@Component({selector: 'link-cmp', template: `<a [routerLink]="['../simple']">link</a>`})
class RelativeLinkCmp {
}

it('should ignore empty path when in corrected mode',
fakeAsync(inject([Router], (router: Router) => {
router.relativeLinkResolution = 'corrected';
@NgModule({
declarations: [RelativeLinkCmp],
imports: [RouterModule.forChild([
{path: 'foo/bar', children: [{path: '', component: RelativeLinkCmp}]},
])]
})
class LazyLoadedModule {
}

const fixture = createRoot(router, RootCmp);
const fixture = createRoot(router, RootCmp);

router.resetConfig([{path: 'lazy', loadChildren: () => LazyLoadedModule}]);
router.resetConfig([{path: 'lazy', loadChildren: () => LazyLoadedModule}]);

router.navigateByUrl('/lazy/foo/bar');
advance(fixture);
router.navigateByUrl('/lazy/foo/bar');
advance(fixture);

const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/lazy/foo/simple');
})));
});
const link = fixture.nativeElement.querySelector('a');
expect(link.getAttribute('href')).toEqual('/lazy/foo/simple');
})));
});

describe('Custom Route Reuse Strategy', () => {
Expand Down

0 comments on commit 7b89d95

Please sign in to comment.