Angular: Reference query params in redirectTo - javascript

I would like to provide a path that redirects to a given page based on query parameters. For example:
/redirect?page=hero&id=1
should redirect to:
/hero/1
Is there any way to do this in the route config? Something like:
{ path: 'redirect?page&id', redirectTo: ':page/:id' }
I can get the redirectTo to respect path parameters but not query parameters. Any ideas?

You can try to use redirectTo: '/:page/:id' and provide extracted from your URL page and id values using custom UrlMatcher():
...
const appRoutes: Routes = [
{
path: 'hero/:id',
component: TestComponent
},
{
matcher: redirectMatcher,
redirectTo: '/:page/:id'
}
];
...
/**
* custom url matcher for router config
*/
export function redirectMatcher(url: UrlSegment[]) {
if (url[0] && url[0].path.includes('redirect')) {
const path = url[0].path;
// sanity check
if (path.includes('page') && path.includes('id')) {
return {
consumed: url,
posParams: {
page: new UrlSegment(path.match(/page=([^&]*)/)[1], {}),
id: new UrlSegment(path.match(/id=([^&]*)/)[1], {})
}
}
}
}
return null;
}
STACKBLITZ: https://stackblitz.com/edit/angular-t3tsak?file=app%2Ftest.component.ts
There is another issue when using redirectTo: ..., active link is not updated, actually isActive flag is not set to true, it is seen on my stackblitz when acrive redirection links are not colored in red

No, there is no way of doing it by a configuration. YOu see, Angular's router does not explicitly define query parameters - any url can have an arbitrary number of query params, and the paths '/page/id' and '/page/id?key=value' are treated as the same in Angular and do map to the same component. There are other, more cumbersome workarounds. One is to create a dummy component and redirect based on ActivatedRoute.queryParams Observable from the component's ngOnInit method. You can easily see why this is a bad idea.
Another way is to create a resolver, this way you maybe can dismiss the component declaration and just redirect from the resolver, again, based on the ActivatedRoute.queryParams Observable, which seems cleaner.
But I do not really get why one would need such a route in a front end application, if you want someone to visit '/page/id', then just navigate them to the page, without any intermediary tricks.

Related

Changing a parameter in Vue 3 router

I was trying to change a route parameter with Vue router.
My route is the following :
/document/:id?/:lang?/edit
Id and lang (language) are the parameters.
I want to change the parameter lang using JavaScript without altering the rest of the route. I tried applying the following solution (found here). The solution was made for Vue 2 and not 3. So I tried to make an updated version.
This is my code :
let params = this.$route.params
params.lang = 'en'
this.$router.push({params:params})
When I execute my code, there is no change in the route parameters nor an error logged in the console. Does anyone know a solution to this problem in Vue 3.
Thanks in advance.
It's a bad way to change your route parameter like this.
Instead, replace your route with the same name, same id, but different lang:
this.$router.replace({
name: this.$route.name,
params: {
id: this.$route.params.id,
lang: 'en' // or whatever you want
}
})
Don't forget to watch for the route changes if needed:
watch: {
$route(to, from) {
// if anything needs to be done when the route changes
}
}

Handling Dynamic URL Params via RouterLink in Angular App

In my Angular 2 app I have a tab area where users can select from a group of independent, but contextually related components. When they click on one of these links, the relevant component loads according to what's defined in the routerLink, like this:
<a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
This was working well. However, since then we've built the app to save various user-selected filter selections as params in the URL. This way when they re-load the component, they'll have their most recent selections still visible and applied to the data. So the URL might look like this after the user had made some filter selections:
http://somesite.com/page1;language_filter=true;language_selection=English
The component code for this looks like this:
public changePage(page, value, type, body)
{
this.onUserSelection(value, type, body, page);
this.route.params.subscribe(
(params: any) => {
this.page = params['page'];
this.language_filter = params['language_filter'];
this.language_selection = params['language_selection'];
}
);
this.router.navigate(
['/page1', {
page: page,
language_filter: this.language_filter,
language_selection: this.language_selection,
}]);
}
This works well for the main navigation methods, that are accomplished via a routing file, where each one looks like this:
{ path: 'page1', component: Page1Component, canActivate: [AuthGuard], data: {contentId: 'page1'} }
However, for this tab area I mentioned, it's loading components according to a hard-coded routerLink param. So I realize now that when a user navigates BACK to a component that way, as opposed to via one of the other ways we make available, it actually overrides the URL params - because it's literally loading "page1" -- because of this <a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
... and thus the URL params that had been added previously are wiped out.
So, my question is, is there a way I can edit this code:
<a class="page-content-header-item" routerLink="/page1" routerLinkActive="selected">Page 1</a>
... so it allows for some dynamic variables? Or do I have to find a new way to handle the navigation in this tab area?
Here is the solution I came to using queryParams.
First, you can pass parameters in your routerLink directive using the queryParams directive:
<a routerLink="/page1" [queryParams]="fooParams">Page 1</a>
<a routerLink="/page2">Page 2</a>
<router-outlet></router-outlet>
where fooParams is a plain object:
export class MainComponent {
fooParams = {
language_filter: true,
language_selection: 'english'
}
}
Angular will output this url like
href="localhost:4200/page1?language_filter=true&language_selection=english"
What you have to do next, is set up a way to intercept the ActivatedRoute, so you can extract the values of the params from the ActivatedRouteSnapshot. You can do this either using a resolver or directly in your component. In my example I used a resolver:
#Injectable()
export class RoutingResolve implements Resolve<any> {
resolve(routeSnapshot: ActivatedRouteSnapshot) {
const { language_filter, language_selection } = routeSnapshot.queryParams;
return { language_filter, language_selection };
}
}
And then pass that resolver in the route definition:
const ROUTES: Routes = [
{
path: 'page1',
component: PageOneComponent,
// In this route we define a resolver so we can intercept the
// activatedRoute snapshot before the component is loaded
resolve: {
routing: RoutingResolve
}
},
{
path: 'page2',
component: PageTwoComponent
}
];
Then, inside PageOneComponent you can subscribe to the resolved data and do whatever you want with it, like for example, setting the value of a form with it and updating the queryParams on form change.
For the full example, check out this plunker
Make sure to open the preview in a separate window, so you can see the route changes in your browser.
If you navigate to Page 1, change the values in the form, then navigate to Page 2, and then press back in your browser, you can see the values loaded in the form correspond to the params in the url.

Angular 2: how do I get route parameters from CanLoad implementation?

I've added a canLoad guard to a state that's lazy loaded. The problem that I'm having is that I can't get any route parameters if the state is being initialized from a different state using router.navigate().
So here is my route configuration:
path: 'programs/:programId/dashboard',
loadChildren: './program.module#ProgramModule',
canLoad: [ProgramGuard]
and this is the short version of ProgramGuard:
export class ProgramGuard implements CanLoad {
canLoad(route: Route): Observable<boolean> {
//route object doesn't have any reference to the route params
let programId = paramFromRoute;
return Observable.create(observer => {
if (programId == authorizedProgramId)
observer.complete(true);
else
observer.complete(false);
}
}
}
I have tried injecting ActivatedRoute to try to get them from there to get it from there, but nothing.
If the user types the URL in the browser, then there is no problem because I can extract the parameters from the location object. But when using route.navigate, the browser's location is still set to the previous state.
Any help or ideas will be greatly appreciated.
I tried to do something similar and ended up changing to a canActivate guard instead. Note also that the canLoad guards block any preloading that you may want to do.
In theory, if a user could not access a route, it would be great to not even load it. But it practice it seems to be too limited to allow making a determination.
Something you could try (I didn't think of it earlier when I was trying to do this) ... you could add a parent route (component-less) that has a canActivate guard that can check the parameters. Then route to the lazy loaded route if the user has authorization.
I was able to retrieve the path including the route parameters using The Location object.
canLoad() {
//dont even load the module if not logged in
if (!this.userSessionService.isSessionValid()) {
this.userSessionService.redirectUrl = this.location.path();
this.router.navigate(['/auth']);
return false;
}
return true;
}
You just need to inject the Location object in the constructor.
Now you can access the queryparams by using this snippet
this.router.getCurrentNavigation().extractedUrl.queryParams
inside the canLoad method and without losing lazyloading feature
I know its too late, but I found the solution that work like charm.
I hope this will help new members who face the same problem like me
canLoad(
route: Route,
segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
if (!this.auth.isLoggedIn()) {
this.route.navigate(['auth/login'], { queryParams: { redirect_url: '/' + segments[0].path } });
return false;
}
return true;
}
Why not building the url from the paths of the segments?
/**
* Test if the user as enough rights to load the module
*/
canLoad(route: Route, segments: UrlSegment[]): boolean | Observable<boolean> | Promise<boolean> {
// We build the url with every path of the segments
const url = segments.map(s => s.path).join('/')
// We handle the navigation here
return this.handleNavigation(url, route)
}
first you can declare variable Like the following :
routeSnapshot: ActivatedRouteSnapshot;
then in constructor call ActivatedRouteSnapshot class Like the following :
constructor(private routeSnapshot: ActivatedRouteSnapshot)
now you can use this.routeSnapshot into canLoad method

How to do a route alias within a Backbone Router?

Having a route like 'dogs': 'process', I need to rewrite it to 'animals': 'process'.
Now, I need the router to recognize both routes, but always display the url like /animals, it is sort of aliasing, but could not find any info on how to solve this without placing an url redirect in 'process' handler.
I'm assuming that the real need for aliases is different than dogs to animals, so I'll answer regardless of if the use-case here is good or not. But if you don't want to change the hash but want to trigger different behaviors in the app, using the router is probably not the route to go.
Route aliases don't really exist in Backbone, other than defining different routes using the same callback. Depending on your exact use-case, there are multiple ways to handle similar routes.
Replace the hash
To display the same hash for a generic route coming from different routes, use the replace option of the navigate function.
routes: {
'lions': 'animalsRoute',
'animals': 'animalsRoute'
},
animalsRoute: function() {
this.navigate("#/animals", { replace: true });
// or using the global history object:
// Backbone.history.navigate("#/animals", { replace: true });
}
then handle the animals route, regardless of which route was initially used to get in this callback.
Some other answers or tutorials will say to use window.location.hash but don't. Manually resetting the hash will trigger the route regardless and may cause more trouble than it'll help.
Different behaviors but showing the same route
Just use different callbacks, both using the replace trick above.
routes: {
'lions': 'lionsRoute',
'tigers': 'tigersRoute'
},
showGenericRoute: function() {
this.navigate("#/animals", { replace: true });
},
tigersRoute: function() {
this.showGenericRoute();
// handle the tigers route
},
lionsRoute: function() {
this.showGenericRoute();
// handle the lions route
}
Notice the inexistent animalsRoute. You could add the route if there's a generic behavior if no specific animal is chosen.
Use the route params
If you want to know which animal was chosen but still use the same callback and remove the chosen animal from the hash, use the route params.
routes: {
'animals/:animal': 'animalsRoute',
},
animalsRoute: function(animal) {
// removes the animal from the url.
this.navigate("#/animals", { replace: true });
// use the chosen animal
var view = new AnimalView({ type: animal });
}
Redirect to the generic route
If you want a different behavior but always show the same route, use different callbacks, then redirect. This is useful if the generic route is in another router instance.
var Router = Backbone.Router.extend({
routes: {
'animals': 'animalsRoute'
},
animalsRoute: function() {
// handle the generic behavior.
}
});
var PussyRouter = Backbone.Router.extend({
routes: {
'lions': 'lionsRoute'
// ...
},
lionsRoute: function() {
// handle lions, then redirect
this.navigate("#/animals", { trigger: true, replace: true });
}
});
Using the trigger options will call the animalsRoute in the other router and the replace option will avoid making an entry in the history, so pushing the back button won't go to lions to get back to animals and being caught in the animals route.

How to get query params in root component?

In angular 2, is it possible to get the query params in the root component in an "angular way"?
I have a simple plnkr with a root component and two routes/states.
I try to get the parameters with :
export class App implements OnInit {
constructor(private route: ActivatedRoute) {
}
ngOnInit(){
console.log(this.route.snapshot.queryParams);
}
}
If I open the example with a query parameter : plnkr the object in the console log is empty.
But when I click the "One" or "Two" links, it can get the query parameters.
I suppose the root can't see the query parameters because it's not an activated route.
Would the solution be to create a fake root route? Or is there another way to read the url (not talking about native JS).
My use case : Regardless of the route I'm on, I need to be able to check if there is a "token" in the url, read from it and then remove it.
Have a look at my change:
https://plnkr.co/edit/b45VUvfVqUl5u2EuzNI9?p=preview
{
path: '**',
component: App
}
I've added a default route - which should be added to the router. This basically says for any other route passed in then go here.
<a routerLink="**" [queryParams]="{token:12345678}">Home</a>
In the links i added a home link, similar to your other links. When you navigate to one or two, then back to home you will see the token passed in the object.
This is because the token was specified in the link.
This only really works if the link will have the token, either hard coded or dynamically generated by another component.
This might help:
https://angular.io/docs/ts/latest/guide/router.html#!#query-parameters

Categories