How to inject Document in service?

TypescriptAngular

Typescript Problem Overview


I have an Angular 2 application. For mocking the Document object in tests, I'd like to inject it to the service like:

import { Document } from '??' 

@Injectable()
export class MyService {
  constructor(document: Document) {}
}

The Title service of Angular uses the internal getDOM() method.

Is there any simple way to inject the Document to the service? Also, how should I reference it in the providers array?

Typescript Solutions


Solution 1 - Typescript

This has been supported by Angular for a while.

You can use the DOCUMENT constant provided by the @angular/common package.

Description of the DOCUMENT constant (taken from the API documentation):

> A DI Token representing the main rendering context. In a browser, this is the DOM Document.

An example is as shown below:

my-service.service.ts:
import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable()
export class MyService {
  constructor(@Inject(DOCUMENT) private document: Document) {}
}
my-service.service.spec.ts
import { provide } from '@angular/core';
import { DOCUMENT } from '@angular/common';

import { MyService } from './my-service';

class MockDocument {}

describe('MyService', () => {
  beforeEachProviders(() => ([
    provide(DOCUMENT, { useClass: MockDocument }),
    MyService
  ]));

  ...
});

Solution 2 - Typescript

I'm unable to comment directly on adamdport's question (not yet 50 rep points), but here it is as stated in the angular docs.

> Blockquote @GünterZöchbauer it looks like DOCUMENT is deprecated. Any idea how to do this once it's gone? For example, how would I set the favicon dynamically?

Instead of importing from platform browser like so:

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

Import it from angular common:

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

Solution 3 - Typescript

in addition to @Günter Zöchbauer's answer.

Angular define DOCUMENT as an InjectionToken

export const DOCUMENT = new InjectionToken<Document>('DocumentToken');

dom_tokens.ts

And inject it with document in browser.ts

{provide: DOCUMENT, useFactory: _document, deps: []}


export function _document(): any {
  return document;
}

Therefore, when we use it, we just need to inject @Inject(DOCUMENT)

or use the token directly in deps:[DOCUMENT]

Solution 4 - Typescript

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

@Injectable()
export class MyService {
  constructor(@Inject(DOCUMENT) private document) {}
}

It's the ": Document" that's causing the problem.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRJoView Question on Stackoverflow
Solution 1 - TypescriptGünter ZöchbauerView Answer on Stackoverflow
Solution 2 - TypescriptRuud VoostView Answer on Stackoverflow
Solution 3 - TypescriptmaxisamView Answer on Stackoverflow
Solution 4 - TypescriptA Concerned Linux DenizenView Answer on Stackoverflow