I wrote an Angular2 (v2.0.1) application that makes use of the router. The website is loaded with several query string parameters, so the full URL initially looks like this:
capture the original url in -base href-
https://example.com/order?id=123
then it will presist
https://example.com/order?id=123#/product
If you are navigating using HTML template then you can use
<a [routerLink]="['/page-2']" [routerLinkActive]="['is-active']" queryParamsHandling="merge">
Something to watch out for is that queryParamsHandling param is without the square brackets.
It turns out the undocumented way to do this without other hacks is to simply remove the leading slash in the "redirectTo" field. Since you are matching the full path you can have the certainty that it'll do what you want (i.e. no surprise url segments) and since it's no longer an absolute target, Angular will preserve the current query parameters.
So in this case
{
path: '',
redirectTo: '/comp1',
pathMatch: 'full'
}
becomes:
{
path: '',
redirectTo: 'comp1',
pathMatch: 'full'
}
Source: https://github.com/angular/angular/issues/13315
Günter Zöchbauer's answer should work properly but, for some reason, it is not working for me at all. What did end up working was passing the queryParams
directly instead of 'preserving' them.
This is what my guard looks like:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
(...)
this.router.navigate(['login'], { queryParams: route.queryParams });
}
There is a workaround using secondary routes as Angular will persist these across primary route navigation.
First, add a named router outlet in your top component:
<router-outlet name="params"><router-outlet>
Next, create a dummy component to route to:
@Component({
template: ""
})
export class ParamsComponent {}
and define a route to instantiate this component into the named outlet:
{
path: ':val1',
component: ParamsComponent,
outlet: "params"
}
Change you app navigation to:
https://my.application.com/(params:val1)
If you look at any ActivatedRoute, you can find the "params" route using:
var paramsRoute = this.activatedRoute.route.children.find(r => r.outlet == "params");
If paramsRoute is null, the url doesn't contain the (params:val1).
This next part gets a bit "hacky" as the secondary route is instantiated after the primary route on initial load. Because of this, until your app is fully loaded, you may find paramsRoute.snapshot to be null. There is a private property "_futureSnapshot" which will contain the route params on initial startup...and persists through the life of the app. You can get to these by using:
var queryParams =
paramsRoute
? paramsRoute["_futureSnapshot"].params
: {};
var val1 = queryParams["val1"];
Given that _futureSnapshot is not part of the public API, this is probably a field we're not supposed to use. If you feel icky using it, you could probably subscribe to paramsRoute.params, but this will probably complicate your components.
if (paramsRoute) {
paramsRoute.params.subscribe(params => {
this.queryParams = params;
this.loadData();
});
} else {
this.queryParams = {};
this.loadData();
}
========= AMENDMENT =============
I found an even better way to pull the query parameters which is definitely NOT icky... In a component or service which is instantiated before routing occurs, add the following logic:
const routeRecognizedSubscription = this.router.events
.filter(e => e instanceof RoutesRecognized)
.subscribe((e: RoutesRecognized) => {
const paramsRoute = e.state.root.children.find(r => r.outlet == "params");
if (paramsRoute) {
// capture or use paramsRoute.params
}
routeRecognizedSubscription.unsubscribe();
});
This code temporarily subscribes to RoutesRecognized events which occur before navigation. After it receives the first event, it will automatically unsubscribe as we only need to do this when the app starts.
On the first event, we look for the state corresponding to "params" outlet. If found, the params property will contain the data we need. No need to access private properties.
I don't think there is a way to define that in the routes configuration.
Currently it is supported for routerLink
s and imperative navigation to enable
You can add a guard to the empty path route, where in the guard navigation to the /comp1
route is done.
router.navigate(['/comp1'], { preserveQueryParams: true }); //deprecated see update note
router.navigate(['/comp1'], { queryParamsHandling: "merge" });
There is a PR to allow to configure preserveQueryParams
globally.
Update note: from https://angular.io/api/router/NavigationExtras, preserveQueryParams is deprecated, use queryParamsHandling instead