feat(router): allow passing `state` to routerLink directives (#27198)
This value will get written to the `history.state` entry. FW-613 (related) Related to #24617 PR Close #27198
This commit is contained in:
parent
67f4a5d4bd
commit
73f6ed9be1
|
@ -77,6 +77,27 @@ import {UrlTree} from '../url_tree';
|
||||||
* </a>
|
* </a>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* You can provide a `state` value to be persisted to the browser's History.state
|
||||||
|
* property (See https://developer.mozilla.org/en-US/docs/Web/API/History#Properties). It's
|
||||||
|
* used as follows:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <a [routerLink]="['/user/bob']" [state]="{tracingId: 123}">
|
||||||
|
* link to user component
|
||||||
|
* </a>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* And later the value can be read from the router through `router.getCurrentTransition.
|
||||||
|
* For example, to capture the `tracingId` above during the `NavigationStart` event:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* // Get NavigationStart events
|
||||||
|
* router.events.pipe(filter(e => e instanceof NavigationStart)).subscribe(e => {
|
||||||
|
* const transition = router.getCurrentTransition();
|
||||||
|
* tracingService.trace({id: transition.extras.state});
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* The router link directive always treats the provided input as a delta to the current url.
|
* The router link directive always treats the provided input as a delta to the current url.
|
||||||
*
|
*
|
||||||
* For instance, if the current url is `/user/(box//aux:team)`.
|
* For instance, if the current url is `/user/(box//aux:team)`.
|
||||||
|
@ -104,6 +125,7 @@ export class RouterLink {
|
||||||
@Input() skipLocationChange !: boolean;
|
@Input() skipLocationChange !: boolean;
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
@Input() replaceUrl !: boolean;
|
@Input() replaceUrl !: boolean;
|
||||||
|
@Input() state?: {[k: string]: any};
|
||||||
private commands: any[] = [];
|
private commands: any[] = [];
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
private preserve !: boolean;
|
private preserve !: boolean;
|
||||||
|
@ -185,6 +207,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
|
||||||
@Input() skipLocationChange !: boolean;
|
@Input() skipLocationChange !: boolean;
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
@Input() replaceUrl !: boolean;
|
@Input() replaceUrl !: boolean;
|
||||||
|
@Input() state?: {[k: string]: any};
|
||||||
private commands: any[] = [];
|
private commands: any[] = [];
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
// TODO(issue/24571): remove '!'.
|
// TODO(issue/24571): remove '!'.
|
||||||
|
@ -237,6 +260,7 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
|
||||||
const extras = {
|
const extras = {
|
||||||
skipLocationChange: attrBoolValue(this.skipLocationChange),
|
skipLocationChange: attrBoolValue(this.skipLocationChange),
|
||||||
replaceUrl: attrBoolValue(this.replaceUrl),
|
replaceUrl: attrBoolValue(this.replaceUrl),
|
||||||
|
state: this.state
|
||||||
};
|
};
|
||||||
this.router.navigateByUrl(this.urlTree, extras);
|
this.router.navigateByUrl(this.urlTree, extras);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1878,6 +1878,36 @@ describe('Integration', () => {
|
||||||
|
|
||||||
expect(location.path()).toEqual('/team/22/simple?q=1#f');
|
expect(location.path()).toEqual('/team/22/simple?q=1#f');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should support history state',
|
||||||
|
fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => {
|
||||||
|
const fixture = createRoot(router, RootCmp);
|
||||||
|
|
||||||
|
router.resetConfig([{
|
||||||
|
path: 'team/:id',
|
||||||
|
component: TeamCmp,
|
||||||
|
children: [
|
||||||
|
{path: 'link', component: LinkWithState},
|
||||||
|
{path: 'simple', component: SimpleCmp}
|
||||||
|
]
|
||||||
|
}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/team/22/link');
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
const native = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(native.getAttribute('href')).toEqual('/team/22/simple');
|
||||||
|
native.click();
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
expect(fixture.nativeElement).toHaveText('team 22 [ simple, right: ]');
|
||||||
|
|
||||||
|
// Check the history entry
|
||||||
|
const history = (location as any)._history;
|
||||||
|
|
||||||
|
expect(history[history.length - 1].state.foo).toBe('bar');
|
||||||
|
expect(history[history.length - 1].state).toEqual({foo: 'bar', navigationId: history.length});
|
||||||
|
})));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('redirects', () => {
|
describe('redirects', () => {
|
||||||
|
@ -4576,6 +4606,13 @@ class RelativeLinkCmp {
|
||||||
class LinkWithQueryParamsAndFragment {
|
class LinkWithQueryParamsAndFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'link-cmp',
|
||||||
|
template: `<a [routerLink]="['../simple']" [state]="{foo: 'bar'}">link</a>`
|
||||||
|
})
|
||||||
|
class LinkWithState {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'simple-cmp', template: `simple`})
|
@Component({selector: 'simple-cmp', template: `simple`})
|
||||||
class SimpleCmp {
|
class SimpleCmp {
|
||||||
}
|
}
|
||||||
|
@ -4770,6 +4807,7 @@ class LazyComponent {
|
||||||
RelativeLinkCmp,
|
RelativeLinkCmp,
|
||||||
DummyLinkWithParentCmp,
|
DummyLinkWithParentCmp,
|
||||||
LinkWithQueryParamsAndFragment,
|
LinkWithQueryParamsAndFragment,
|
||||||
|
LinkWithState,
|
||||||
CollectParamsCmp,
|
CollectParamsCmp,
|
||||||
QueryParamsAndFragmentCmp,
|
QueryParamsAndFragmentCmp,
|
||||||
StringLinkButtonCmp,
|
StringLinkButtonCmp,
|
||||||
|
@ -4797,6 +4835,7 @@ class LazyComponent {
|
||||||
RelativeLinkCmp,
|
RelativeLinkCmp,
|
||||||
DummyLinkWithParentCmp,
|
DummyLinkWithParentCmp,
|
||||||
LinkWithQueryParamsAndFragment,
|
LinkWithQueryParamsAndFragment,
|
||||||
|
LinkWithState,
|
||||||
CollectParamsCmp,
|
CollectParamsCmp,
|
||||||
QueryParamsAndFragmentCmp,
|
QueryParamsAndFragmentCmp,
|
||||||
StringLinkButtonCmp,
|
StringLinkButtonCmp,
|
||||||
|
@ -4826,6 +4865,7 @@ class LazyComponent {
|
||||||
RelativeLinkCmp,
|
RelativeLinkCmp,
|
||||||
DummyLinkWithParentCmp,
|
DummyLinkWithParentCmp,
|
||||||
LinkWithQueryParamsAndFragment,
|
LinkWithQueryParamsAndFragment,
|
||||||
|
LinkWithState,
|
||||||
CollectParamsCmp,
|
CollectParamsCmp,
|
||||||
QueryParamsAndFragmentCmp,
|
QueryParamsAndFragmentCmp,
|
||||||
StringLinkButtonCmp,
|
StringLinkButtonCmp,
|
||||||
|
|
Loading…
Reference in New Issue