56

Given this url structure (over which I have no control), how can I retrieve the hash fragment using Angular2?

http://your-redirect-uri#access_token=ACCESS-TOKEN

My router does route to the correct component, but everything after oauth get scrapped and I can't find the hash fragment in request.params or location.path. Doomed??

Router config:

@RouteConfig([
{path: '/welcome', name: 'Welcome', component: WelcomeComponent, useAsDefault: true},
{path: '/landing/oauth', name: 'Landing', component: LandingComponent}  // this one

])

10 Answers 10

86

For those still looking :

import { ActivatedRoute } from '@angular/router';

export class MyComponent {

  constructor(
    private route: ActivatedRoute,
  ) { }

  myfunction(){
    this.route.fragment.subscribe((fragment: string) => {
        console.log("My hash fragment is here => ", fragment)
    })
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

No need to worry about unsubscribing from ActivatedRoute observables: > The ActivatedRoute and its observables are insulated from the Router itself. The Router destroys a routed component when it is no longer needed and the injected ActivatedRoute dies with it.
21

To expand on the current answers, I wanted to address an easy way to parse the query params in the hash (specifically for a federated response) since the ActivatedRoute doesn't seem to handle that natively.

this.route.fragment.subscribe(fragment => {
  const response = _.fromPairs(Array.from(new URLSearchParams(fragment)));
  response.access_token;
  response.id_token;
  response.expires_in;
  response.token_type;
});

First create a new URLSearchParams object with the fragment to query for its values:

new URLSearchParams(fragment).get('access_token');

For most cases this is probably all that is needed, but if converting this to an object is desired, Array.from converts URLSearchParams into an array of arrays that looks like: [['key', 'value'], ...]. Then lodash's _.fromPairs converts this to an object.

2 Comments

The next step after finding this.route.fragment to get the fragment is to search how to parse its queries. Thanks for adding URLSearchParams snippet!
Great Answer! I was able to grab the fragment but I was left with multiple params to parse through. Your answer saved me a ton of time!
15

you can also use ActivatedRouteSnapshot with no need to subscribe for all changes on it.

@Component({templateUrl:'./my-component.html'})
class MyComponent {
  constructor(route: ActivatedRoute) {
    const fragment: string = route.snapshot.fragment;
  }
}

2 Comments

@MochamadArifin I think that what you need.
Awesome, this is the most simplest solution thanks :)
5

I've taken the comment from nwayve and implemented it using RxJS pipes like this:

this.route.fragment
  .pipe(
    map(fragment => new URLSearchParams(fragment)),
    map(params => ({
      access_token: params.get('access_token'),
      id_token: params.get('id_token'),
      error: params.get('error'),
    }))
  )
  .subscribe(res => console.log('', res));

2 Comments

Note that if you have OAuth2.0 access tokens they can include + which will become a whitespace character in your access token.
@DanielB if you have an access token "4XVjonKI1k+" with a plus sign, and the code above is getting the access token from a URL encoded string then the access token will have a space like ""4XVjonKI1k " instead of the +? To fix this we need to add decodeURIComponent(params.get("access_token"))? Did I understand this correctly?
2

Assuming you're using ActivatedRoute class in your constructor, try this:

let params = this.route.snapshot.fragment;

const data = JSON.parse(
    '{"' +
        decodeURI(params)
            .replace(/"/g, '\\"')
            .replace(/&/g, '","')
            .replace(/=/g, '":"') +
        '"}'
);

console.log(data); // { json: "with your properties"}

Comments

1

I had the same problem by requesting OAuth server with response_type=token, and who redirects to %REDIRECT_URI%#access_token=:access_token&token_type=:token_type&expires_in=:expires_in.

The problem is, by default, the direct access to sub-url is not routed: in your case, %BASE_URL%/landing/oauth will not be redirect to LandingComponent component.

I fixed it with this configuration:

import { bootstrap } from '@angular/platform-browser-dynamic';
import { provide } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { ROUTER_PROVIDERS } from '@angular/router';

import { AppComponent } from './components/app/app.component';

bootstrap(AppComponent, [
    ROUTER_PROVIDERS,
    provide(APP_BASE_HREF, { useValue: '/' }) // this line
]);

Comments

1

An option for parsing the fragment data with plain javascript is

this.activatedRoute.fragment.subscribe(value => {
  let fragments = value.split('&')
  let fragment = {}
  fragments.forEach(x => {
    let y = x.split('=')
    fragment[y[0]] = y[1]
  })
})

The information will be in the form of an object which can be easily accessed.

Comments

1

I tried this, but the snapshot was an empty string. https://stackoverflow.com/a/51286916/569302

Here's what worked for me:

  ngOnInit(): void {
    this.route.fragment.subscribe({ 
        next: value => {
        if (value === null) {
          throw new Error('not implemented');
        }
        const access_token = new URLSearchParams(value).get('access_token')
        console.log({access_token})
      }
    });
  }

Comments

0

You can retrieve the url fragment at any time using the following code - which makes use of the Router service:

const urlTree = this.router.parseUrl(this.router.url);
console.log(urlTree.fragment); //access_token=ACCESS-TOKEN

Comments

0

This was the only 'angular only' solution that worked for me (

import { NavigationEnd, Router } from '@angular/router';
...

constructor(
    private router: Router,) {}
...

ngOnInit() { 
    this.router.events.subscribe((event) => {
      if(event instanceof NavigationEnd) {
        console.log("route change url", event.url)
      }
    });
}

from a URL of http://localhost:8100/#/activity prints /activity

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.