I need to get the full base URL (e.g. http://localhost:5000 or https://productionserver.com) of my Angular 2 app so that I can pass it along to a 3rd party service in the co
You’ll find that all content coming from Http requests won’t be pre-rendered: it’s because Universal needs absolute URLs.
As your development and production server won’t have the same URL, it’s quite painful to manage it on your own.
My solution to automate this : using the new HttpClient interceptor feature of Angular 4.3, combined with the Express engine.
The interceptor catches all requests when in server context to prepend the full URL.
import { Injectable, Inject, Optional } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from'@angular/common/http';
@Injectable()
export class UniversalInterceptor implements HttpInterceptor {
constructor(@Optional() @Inject('serverUrl') protected serverUrl: string) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const serverReq = !this.serverUrl ? req : req.clone({
url: ``${this.serverUrl}${req.url}``
});
return next.handle(serverReq);
}
}
Then provide it in your AppServerModule :
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UniversalInterceptor } from './universal.interceptor';
@NgModule({
imports: [
AppModule,
ServerModule
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: UniversalInterceptor,
/* Multi is important or you will delete all the other interceptors */
multi: true
}],
bootstrap: [AppComponent]
})
export class AppServerModule {}
Now you can use Express engine to pass the full URL to Angular, just update your server.js :
function angularRouter(req, res) {
res.render('index', {
req,
res,
providers: [{
provide: 'serverUrl',
useValue: `${req.protocol}://${req.get('host')}`
}]
});
}
Now I'm using the server.ts ngExpressEngine:
import { ngExpressEngine } from '@nguniversal/express-engine';
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main.bundle');
const {provideModuleMap} = require('@nguniversal/module-map-ngfactory-loader');
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
And after that I can use in location.service.ts:
constructor(@Optional() @Inject(REQUEST) private request: any,
@Optional() @Inject(RESPONSE) private response: any,
@Inject(PLATFORM_ID) private platformId: Object)
{
if (isPlatformServer(this.platformId))
{
console.log(this.request.get('host’)); // host on the server
} else
{
console.log(document.location.hostname); // host on the browser
}
}
I have bit of working code with angular 5 and angular universal
in server.ts replace this
app.engine('html', (_, options, callback) => {
let engine = ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
{ provide: 'request', useFactory: () => options.req, deps: [] },
provideModuleMap(LAZY_MODULE_MAP)
]
});
engine(_, options, callback);
});
and in Angular side you can get host with below code
export class AppComponent {
constructor(
private injector: Injector,
@Inject(PLATFORM_ID) private platformId: Object
) {
console.log('hi, we\'re here!');
if (isPlatformServer(this.platformId)) {
let req = this.injector.get('request');
console.log("locales from crawlers: " + req.headers["accept-language"]);
console.log("host: " + req.get('host'));
console.log("headers: ", req.headers);
} else {
console.log('we\'re rendering from the browser, there is no request object.');
}
}
}
Thanks to help from estus, I managed to hack together something that works.
It looks like most Angular Universal templates actually have the server passing a zone parameter called "originUrl", which gets used by the server rendering methods to provide a ORIGIN_URL token that can be injected in other methods. I couldn't find any documentation about this, but you can see an example here.
So if you write something like this...
export function getBaseUrl() {
if (Zone.current.get("originUrl")) {
return Zone.current.get('originUrl');
} else if (location) {
return location.origin;
} else {
return 'something went wrong!';
}
}
You should be able to get the full origin URL on both the server and the client.