How to redirect to an external URL from angular2 route without using component?
JavascriptAngularAngular2 RoutingJavascript Problem Overview
I would like to create an external redirect, but to make all routes consistent I think it would be nice to do everything(including external redirects) under Router States configuration.
so:
const appRoutes: Routes = [
{path: '', component: HomeComponent},
{path: 'first', component: FirstComponent},
{path: 'second', component: SecondComponent},
{path: 'external-link', /*would like to have redirect here*/}
];
UPD: and I don't want to use empty component for this case like @koningdavid suggested. This solution looks really weird for me. It should be something really easy to implement for such case, without virtual components.
Javascript Solutions
Solution 1 - Javascript
You can achieve what you want with a trick using the resolve option of a route. Resolve is some data value that Angular2 will obtain for the route to be initialized. More details you can find here in the official documentation.
I have tried this approach and it does work. Example:
Add this to the provider section (plus import the required classes from Routing)
@NgModule({
providers: [
{
provide: 'externalUrlRedirectResolver',
useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) =>
{
window.location.href = (route.data as any).externalUrl;
}
}
]
})
Then you can define your route like this:
{
path: 'test',
component: AnyRandomComponent,
resolve: {
url: 'externalUrlRedirectResolver'
},
data: {
externalUrl: 'http://www.google.com'
}
}
This will redirect to the external URL. It's a bit of a hackish way really. I tried to achieve the result without using the component at all, but you have to use either redirectTo
or component
or children
or loadChildren
. redirectTo
won't trigger the resolve and I am not sure about children, though you can experiment.
You can implement it in a nice class rather than direct function in provider. More info in the documentation (see reference above).
P.S. I would really rather use a redirect component myself I think. Just use the trick with the data and getting the state from the router with externalUrl
to get this as a parameter.
Solution 2 - Javascript
You can create a RedirectGuard:
import {Injectable} from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, Router, RouterStateSnapshot} from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class RedirectGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
window.location.href = route.data['externalUrl'];
return true;
}
}
Import it in app.module:
providers: [RedirectGuard],
And define your route:
{
path: 'youtube',
canActivate: [RedirectGuard],
component: RedirectGuard,
data: {
externalUrl: 'https://www.youtube.com/'
}
}
Solution 3 - Javascript
As far as I know NG2 router doesn't support external redirecting. You could create a redirect component as a workaround.
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'redirect',
template: 'redirecting...'
})
export class RedirectComponent implements OnInit {
constructor() { }
ngOnInit() {
window.location.href = 'http://www.redirecturl.com'
}
}
And use that in your routing
{ path: 'login', component: RedirectComponent, pathmath: 'full'},
Solution 4 - Javascript
Hmm...
I think you can simply request the URL instead of calling ng2 Router...
For example...
<a href="http://example.com">External</a>
instead of
<a routerLink="/someRoute" routerLinkActive="active">External</a>
OR
window.location.href = 'http://www.example.com'
instead of
this.router.navigate( [ '/someRoute', 'someParam' ] );
Right...?
Solution 5 - Javascript
The Router can't redirect externally. An external resource can't be a state of the app.
If it's only for clarity, keeping all the routes visible in the one spot, you could define another constant array with all the external paths in the same file as the routes.
Solution 6 - Javascript
just use:
{
path: 'external-link',
loadChildren: () => new Promise( () => { if(window.location.href.match(/external-link/) ) window.location.href = 'https://external-link.com/'; } )
},
Solution 7 - Javascript
I assume you don't wanna create a component for every single url, which is why you are looking to do it without a component...
So you can try creating a function that generates the component object for you...
For example...
function generateLinkingComponent( url ) {
// Generate your component using koningdavid's code
// replace 'http://www.redirecturl.com' with url param
// and return it...
}
And add it like this in your router config...
const appRoutes: Routes = [
{path: '', component: HomeComponent},
{path: 'first', component: FirstComponent},
{path: 'second', component: SecondComponent},
{path: 'external-link', component: generateLinkingComponent( 'http://example.com' )},
{path: 'client-login', component: generateLinkingComponent( 'http://client-login.example.com' )},
{path: 'admin-login', component: generateLinkingComponent( 'http://admin.example.com' )},
];
This will be easy with JS... but not sure how one can return a class in a function in typeScript...
Hope that helps...
Solution 8 - Javascript
Wrapping up Ilya's answer:
Add this module.
import { Component, Injectable, NgModule } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
@Component({
template: ''
})
class ExternalLinkComponent {
constructor() {
}
}
@Injectable()
class ExternalLinkResolver implements Resolve<any> {
resolve(route: ActivatedRouteSnapshot): any {
window.location.href = route.data.targetUri;
return true;
}
}
export class ExternalRoute {
data: {
targetUri: string;
};
path: string;
pathMatch = 'full';
resolve = { link: ExternalLinkResolver };
component = ExternalLinkComponent;
constructor(path: string, targetUri: string) {
this.path = path;
this.data = { targetUri: targetUri };
}
}
@NgModule({
providers: [ ExternalLinkResolver ],
declarations: [ExternalLinkComponent]
})
export class ExternalRoutesModule { }
Then import ExternalRoutesModule
and add instances of ExternalRoute.
const childRoutes: Routes = [
new ExternalRoute('', '/settings/account'),
{ path: 'staff-profiles', component: StaffProfilesComponent},
{ path: 'staff-assignments', component: StaffAssignmentsComponent}
];
const routes: Routes = [
{ path: '', component: BaseComponent, children: childRoutes }
];
@NgModule({
imports: [ ExternalRoutesModule, RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class SettingsRoutingModule { }
Note I'm mounting the submodule routes via loadChildren in this example.
Solution 9 - Javascript
You can use the NavigationEnd event.
import { NavigationEnd, Router } from '@angular/router';
app.component.ts
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if (event.url.includes('faq')) {
// open in the same tab:
window.location.href = 'https://faq.website.com';
// open a new tab:
// window.open('https://faq.website.com', '_blank');
// and redirect the current page:
// this.router.navigate(['/']);
}
}
});
P.S. Don't forget to remove your route from the AppRoutingModule.
Solution 10 - Javascript
Here is a code that should work for you without a lot of issues. FYI the router events error handler can be put anywhere irrespective of placement in the component.
app.component.html
Angular Port is in 4200
<a routerLink="/test">Main Link - 1</a> |
<a [routerLink]="getLinkWithExternal({url: '/test', external:false})">Other Link - 1a</a> |
<a [routerLink]="getLinkWithExternal({url: 'http://localhost:4211', external:true})">Other Link - 1b</a> |
<a [routerLink]="getLink({url: '/test'})">Other Link - 1a</a> |
<a [routerLink]="getLink({url: 'http://localhost:4211'})">Other Link - 1b</a> |
<a style="cursor: pointer; text-decoration: underline;" (click)="routeLink('/test')">Other Link - 1c</a> |
<a style="cursor: pointer; text-decoration: underline;" (click)="routeLink('http://localhost:4211')">Other Link - 1d</a>
<router-outlet></router-outlet>
app.component.ts
import { Component } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(private router: Router) { }
// RECOMMENDATION - Add following in menus of each microservice (individual and different)
// external: true
// Will make it a better menu structure for each microservice
// If Single and same menu for all microservices then remove external === true
// Logic One
getLinkWithExternal(sidebarnavLink: any) {
this.router.errorHandler = function (error: any) {
if (!sidebarnavLink.url.includes(window.location.origin.toString()) && sidebarnavLink.url.includes("http") && sidebarnavLink.external === true) {
window.location.href = sidebarnavLink.url.toString();
return true;
}
return null;
}.bind(sidebarnavLink);
return [sidebarnavLink.url];
}
getLinkWithExternalWithEventSubscribe(sidebarnavLink: any) {
this.router.events.subscribe(function (event) {
if (event instanceof NavigationEnd) {
if (event.url.includes('http')) {
if (!sidebarnavLink.url.includes(window.location.origin.toString()) && sidebarnavLink.url.includes("http") && sidebarnavLink.external === true) {
window.location.href = sidebarnavLink.url.toString();
return true;
}
return this.router.navigateByUrl(sidebarnavLink.url);
// return this.router.navigate([sidebarnavLink.url]);
}
return this.router.navigateByUrl(sidebarnavLink.url);
// return this.router.navigate([sidebarnavLink.url]);
}
}.bind(sidebarnavLink))
}
getLinkWithExternalImplementationTwoWithNoRouteError(sidebarnavLink: any) {
if (!sidebarnavLink.url.includes(window.location.origin.toString()) && sidebarnavLink.url.includes("http") && sidebarnavLink.external === true) {
window.location.href = sidebarnavLink.url.toString();
return true;
}
return [sidebarnavLink.url];
}
// Logic Two
getLink(sidebarnavLink: any) {
this.router.errorHandler = function (error: any) {
if (!sidebarnavLink.url.includes(window.location.origin.toString()) && sidebarnavLink.url.includes("http")) {
window.location.href = sidebarnavLink.url.toString();
return true;
}
return null;
}.bind(sidebarnavLink);
return [sidebarnavLink.url];
}
// Logic Three
routeLink(lnk: any) {
if (lnk.includes("http")) {
console.log("Test");
window.location.href = lnk;
return true;
}
return this.router.navigateByUrl(lnk);
// return this.router.navigate([lnk]);
}
}