2

I have a simple Angular app written in ES2015 not TypeScript.

In AngularJS 1.x we could inject $document to avoid using the document browser global.

How can we do the same in Angular (the docs are all for TypeScript and, amazingly, none of the docs appear to show this fundamental requirement).

My main.js entry file (I'm using Webpack 2.2 also) is like this:

'use strict';

import 'reflect-metadata';
import 'zone.js';

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app/app.module';

let boot = document.addEventListener('DOMContentLoaded', () => {
    platformBrowserDynamic().bootstrapModule(AppModule);
});

export { boot };

Currently this works, but it's using the global document to add the DOMContentLoaded event handler.

How can I inject some kind of Angular wrapper for the document in Angular?

According to the official docs DOCUMENT is an "injectible token", but I can't see any examples in using this. And I don't really have anything to inject it into.

I've tried importing it like this:

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

But now DOCUMENT is just a single object with a desc property?

shammelburg
  • 6,974
  • 7
  • 26
  • 34
danwellman
  • 9,068
  • 8
  • 60
  • 88
  • 1
    There's an example of injecting into a component in ES5 [here](http://stackoverflow.com/questions/38859198/angular-2-dependency-injection-in-es5-and-es6); "injectable token" just means that it's the *name* of the thing, not the thing itself (in @Injectable/@Component cases the thing is its own token/name), so in TS you'd have `@Inject(DOCUMENT) document: Document`. – jonrsharpe May 09 '17 at 12:43

2 Answers2

3

document is used before the application is being bootstrapped. The code above cannot benefit from DOCUMENT provider (nor should it, it would introduce needless abstraction here).

DOCUMENT is supposed to be used as an abstraction for DOM manipulations inside Angular application:

class SomeComponent {
  constructor(@Inject(DOCUMENT) private document) {
  }
  ngOnInit() {
    this.document.querySelector(...)...
  }
}

It is equal to window.document in browser but can be mocked via DI, this is useful in tests or server side applications (the latter use crippled implementation from @angular/platform-server by default).

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • I'd just got to the point where I realised it would not work in the bootstrapping part of the app - I was able to inject `DOCUMENT` into a component, but even after converting the bootstrapping code into a class, it was not working there at all, which makes sense now. Thanks (again!) – danwellman May 09 '17 at 13:46
  • 1
    You're welcome. Fortunately, it's not a problem. main.js is supposed to be executed only with real `document`, so a global can be safely hard-coded there. – Estus Flask May 09 '17 at 13:50
0

I think this answer needs a small update, since the latest way of doing this in Angular 16, following the standalone standard that Angular is heading to, is:

import { DOCUMENT } from '@angular/common';
import { inject } from '@angular/core';

@Component({
  ...
  standalone: true,
  ...
})
class SomeComponent {
  private document = inject(DOCUMENT);

  ngOnInit() {
     this.document.querySelector(...)...
  }
}
MichaelShake
  • 238
  • 2
  • 13