0

From the docs:

For example, your server-side page can't reference browser-only native objects such as window, document, navigator, or location. If you don't need these on the server-rendered page, you can side-step them with conditional logic. Alternatively, you can find an injectable Angular abstraction over the object you need such as Location or Document; it may substitute adequately for the specific API that you're calling. If Angular doesn't provide it, you can write your own abstraction that delegates to the browser API while in the browser and to a satisfactory alternative implementation while on the server.

It makes sense the server can't access browser objects. But it can be achievend:

Alternatively, you can find an injectable Angular abstraction over the object you need such as Location or Document; it may substitute adequately for the specific API that you're calling.

Next:

If Angular doesn't provide it, you can write your own abstraction that delegates to the browser API while in the browser and to a satisfactory alternative implementation while on the server.

Where can I find which ones Angular provides and how can I use them? I'm specificly looking for the navigator.

If navigator is not provided by Angular, how can I write my own abstraction?

Side node: I used ng add @nguniversal/express-engine --clientProject angular.io-example to get started with ssr.

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
  • [This answer](https://stackoverflow.com/a/37176929/1009922) and [this article](https://juristr.com/blog/2016/09/ng2-get-window-ref/) give procedures to make global objects injectable. See [this stackblitz](https://stackblitz.com/edit/angular-ng4-loading-spinner-z5jkrz) for a demo, adapted to the `navigator` object. – ConnorsFan Oct 24 '18 at 20:53
  • Just that will make it work with ssr? – Robin Dijkhof Oct 25 '18 at 07:20

1 Answers1

1

While I wouldn't recommend this approach, if you need the navigator object in Angular Universal, you might want to check the Domino project.

Then, in your server.ts file you would do something like this:

const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();
const win = domino.createWindow(template);

global['window'] = win;
global['Node'] = win.Node;
global['navigator'] = win.navigator;
global['Event'] = win.Event;
global['Event']['prototype'] = win.Event.prototype;
global['document'] = win.document;

And in the handler you could do:

app.engine('html', (_, options, callback) => {
  renderModuleFactory(AppServerModuleNgFactory, {
    // Our index.html
    document: template,
    url: options.req.url,
    // DI so that we can get lazy-loading to work differently (since we need it to just instantly render it)
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  }).then(html => {
    callback(null, html);
  });
});

See: https://mdbootstrap.com/angular/angular-universal/

  • Why wouldn't you recommend this? – Robin Dijkhof Oct 25 '18 at 06:37
  • 1
    We had some issues with the `window` object in the past (Angular 5 and Universal), and you should be aware it is not a complete browser implementation. We ended up just adding checks for most of our code to run conditionally on the browser. – Pablo A. Costesich Oct 25 '18 at 15:51