Parse URL in Angular

Recently, I had a requirement to parse the browser URL into URL parts and extract the query string part in Angular 4 application. With Angular 1.x, it was simple to just inject $location and read $location.search().querystringName.

But, with Angular 2+, these kind of URL parse functions were moved into @angular.router. If your application already requires Routing Module, then go-ahead and simply import & parse url as given below

import { DefaultUrlSerializer } from '@angular/router';?
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';

@Component({
 selector: 'my-component',
 providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }],
})
export class MyComponent{
  constructor(
    private config: ConfigService,
    private location: Location,
    private urlUtility: UrlUtility) { 
    
    var urlObj : UrlTree = new DefaultUrlSerializer(this.location.path());
  }
}

Or else, if you just need a URL parse method features, then Performing above import, will result in entire router library being loaded/included in bundle (unnecessary bloat). Because Angular Routing lib is actually an ES5 - single UMD(Universal Module Definition) but sugar coated using TypeScript definition files, that gives an illusion like ES6 import & exports.

Alternate Solution

So in-order to optimize my application bundle size., I decided to pick up an alternate URL Parsing library from NPM, that should work fine for both in client/browser and NodeJs environments. After a long search, I've settled with url-parse which has a small footprint and performs all required operations on URL string ( It looks to be recent project and still active in development).

// UrlObject.ts
interface IHashTable<T> {
  [key: string]: T;
}

export declare class UrlObject {
  public protocol: string;
  public slashes: boolean;
  public auth: string;
  public username: string;
  public password: string;
  public host: string;
  public hostname: string;
  public port: number;
  public pathname: string;
  public query: IHashTable<string>;
  public hash: string;
  public href: string;
  public origin: string;
  public set(key: string, value: string): void;
  public toString(): string;
}
//url-utility.ts
import { UrlObject } from '../dto';
import { Injectable } from '@angular/core';
import * as urlParse from 'url-parse';

@Injectable()
export class UrlUtility {
  public parse(url: string, parseQuery= true): UrlObject {
      return urlParse(url, parseQuery);
  }
}


Then in your Angular component, you can import above service and use it as shown below

//my-component.ts
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
import { UrlObject } from './dto';

@Component({
  selector: 'my-component',
  styleUrls: [],
  providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }],
})
export class MyComponent{
 constructor(
    private config: ConfigService,
    private location: Location,
    private urlUtility: UrlUtility) { 
    let url: UrlObject = this.urlUtility.parse(this.location.path());
 }
}

When importing or requiring a module, care should be taken to verify if an import is indeed picking up the required piece of code OR does it bring lot of junk & un-used code into bundle. An extra review of generated bundle using Webpack-bundle-Analyzer will tell you a lot on the bloat a require statement would do.

Follow the mantra: IMPORT WITH CARE wink

Related Posts