63

I need to make request to same server, REST API on another port.

How can I do this without hardcoding full name in service URLs?

Dale K
  • 25,246
  • 15
  • 42
  • 71
Pavel Kulikov
  • 871
  • 1
  • 7
  • 10

9 Answers9

87

There's no need for an angular2-specific solution. You can use the window.location.hostname to get the current hostname.

Note, however, if you don't want to use global variables like the window-object directly, you can provide your own Window object, which then can be injected.

See this full working Stackblitz Angular sample for details.

Updated Answer for Angular 6+

As others have stated, the original answer does not work anymore. For Angular 6+, you need to provide an Injection Token, so that the window-object can be resolved in the AOT-build too. Otherwise you will get the error "Can't resolve all parameters".

I recommend creating a WINDOW_PROVIDERS array in a separate file like this:

import { InjectionToken, FactoryProvider } from '@angular/core';

export const WINDOW = new InjectionToken<Window>('window');

const windowProvider: FactoryProvider = {
  provide: WINDOW,
  useFactory: () => window
};

export const WINDOW_PROVIDERS = [
    windowProvider
]

The WINDOW_PROVIDERS constant can be added to the providers array in the AppModule like this:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    WINDOW_PROVIDERS, // <- add WINDOW_PROVIDERS here
    SampleService,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

In the SampleService, the window object can be injected by using the defined Injection Token like this:

import { Injectable, Inject } from '@angular/core';
import { WINDOW } from '../window.provider';

@Injectable()
export class SampleService {

    constructor(@Inject(WINDOW) private window: Window) {
    }

    getHostname() : string {
        return this.window.location.hostname;
    }
}

Original Answer for Angular 2

Therefore you need to set the provider for the Window-object when bootstrapping your application.

import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);

After that you can use the window object and access the hostname like this:

constructor(private window: Window) {
   var hostname = this.window.location.hostname;
}
Raedwald
  • 46,613
  • 43
  • 151
  • 237
M.E.
  • 2,759
  • 3
  • 24
  • 33
  • 7
    What are the advantages of using your own Window object vs using a global constant? Thanks – MartaGalve May 31 '18 at 07:47
  • 2
    This will not work on Angular 6 anymore. You will get an error with `Can't resolve all parameters`. To make this work you have to declare an injection token and then use it in the constructor. I will add an answer to make this more explicit. – Giovanni Bassi Jun 20 '18 at 21:40
  • what about angular universal? this is still working with the window_providers? – Kevin Vincent Sep 24 '18 at 08:40
  • This will fail for 'ng build --prod=true'. To fix this, the useFactory needs to be a normal function. – Stef Heyenrath Feb 05 '19 at 13:47
  • @StefHeyenrath I've tested the prod build and it works for me. I've also added a [Stackblitz sample](https://stackblitz.com/edit/angular-window-hostname) which also works in the prod build. – M.E. Feb 06 '19 at 08:00
  • @M.E. I still get this error: "ERROR in src\app\app.module.ts(32,3): Error during template compile of 'AppModule' Function expressions are not supported in decorators in 'WindowProvider' 'WindowProvider' references 'WindowProvider' 'WindowProvider' references 'WindowProvider' 'WindowProvider' contains the error at src\app\common\utils\window.provider.ts(9,17) Consider changing the function expression into an exported function." I'll try to create a sample project on github. – Stef Heyenrath Feb 06 '19 at 09:56
  • DI just to get the window object...talk about overcomplication. lol – wooters Mar 06 '19 at 01:46
  • 1
    @bubble (and others) The answer to why you would do this through injection is: **Unit Testing**. If, for example, you want to unit test that your method correctly constructs a URI from the location (e.g. origin + pathname + new path) then opens a new window to that path... you need to provide a fake: https://stackoverflow.com/a/40320802/5753629 – Reginald Blue Feb 27 '20 at 20:39
41

Another option is to use DOCUMENT from @angular/platform-browser.

import {DOCUMENT} from '@angular/platform-browser';

constructor(@Inject(DOCUMENT) private document: Document) {
    let url = document.location.protocol +'//'+ document.location.hostname + ':my_port' );
}
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
Thibs
  • 8,058
  • 13
  • 54
  • 85
11

Angular 2 latest working solution:

app.module.ts

providers: [
    {provide: Window, useValue: window},
    ...
]

youclass.ts

constructor(
    @Inject(Window) private _window: Window
) {
    this._baseUrl = `http://${this._window.location.hostname}:3333`;
};
Marfu
  • 405
  • 6
  • 9
11

You can use window, as others have stated, and to make it injectable, from ng6 and forward, you need an injection token.

Declare the token like this:

export const WINDOW = new InjectionToken('window',
    { providedIn: 'root', factory: () => window }
);

Then use it in the class constructor:

class Foo {
  constructor(@Inject(WINDOW) private window: Window) { }
}

As Window is an interface in TypeScript, if you don't do the injection like that, when you build the project for production you will get an error: Can't resolve all parameters for <ClassName>. And later another one: ERROR in : Error: Internal error: unknown identifier undefined.

To understand injection better, read the angular docs for DI: https://angular.io/guide/dependency-injection

Giovanni Bassi
  • 739
  • 6
  • 18
8

I'm using plain javascript and URL native api for URL parsing:

declare var window: any; // Needed on Angular 8+
const parsedUrl = new URL(window.location.href);
const baseUrl = parsedUrl.origin;
console.log(baseUrl); // this will print http://example.com or http://localhost:4200
gianlucaparadise
  • 1,678
  • 20
  • 32
4

I achieved it with following code in my app.component.ts:

import { Component, OnInit, Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

    constructor(@Inject(DOCUMENT) private document: any) { }

    ngOnInit() {
        this.domain = this.document.location.hostname;
        console.log(this.domain);
    }
}

This should print the domain name in your console.

Nilesh G
  • 412
  • 4
  • 17
4

I test this on Angular 7 and it worked correctly

declare var window: any;

console.log (window.location.host); //result lrlucas.github.io > domain github pages

with window.location.host I get the full domain

Note: Declare the window variable before @Component

  • Have you tried your code with `'ng build --prod=true`? – James Poulose May 11 '19 at 20:41
  • 1
    Yes @James Poulose, compile a project of angular with `ng build --prod=true` and run a server with this command `http-server -o -p 8081` and the result of in the console was `127.0.0.1:8081` – Lucas Suarez May 11 '19 at 21:48
3

I recommend using window.location as others have kindly stated.

However, you can also do this by importing Angular's common 'Location' library and using it in an injectable like so:

import { Injectable } from '@angular/core';
import { Location }   from '@angular/common';
const otherPort = 8000;

@Injectable()
export class ServiceOrComponentName {
  constructor(location: Location) {
    this.baseUrl = location._platformStrategy._platformLocation._location.protocol +
      '//' + location._platformStrategy._platformLocation._location.hostname +
      ':' + otherPort;
  }
}
LJH
  • 7,444
  • 3
  • 10
  • 19
2

Just inject Document. Angular has a default implementation for Document. Document has a property called defaultView that has all the goodies like window.

import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class HostnameService
{

  constructor(
    @Inject(DOCUMENT) private document: Document
  ) { }

  getHostname(): string
  {
    // Remember to use this.document as unscoped document also exists but is not mockable.
    return this.document.defaultView.window.location.hostname;

    // Or use the location on document.
    return this.document.location.hostname;

  }
}

There is no setup required elsewhere.

baloozy
  • 39
  • 4