vlio20
vlio20

Reputation: 9295

Making moment injectable in angular2

I would like to make moment to be injectable through out my app.
I just started learning ng2 and couldn't find this type of usage in the docs.
Here is what I have in my app.module.ts:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {AppComponent} from './app.component';
import * as moment from 'moment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [{provide: 'moment', useValue: moment}],
  bootstrap: [AppComponent]
})
export class AppModule {
}

and here is the component:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})


export class AppComponent {
  title = 'app works!';

  constructor(private moment) {
    this.title += this.moment;
  }
}

there is this error:

Uncaught Error: Can't resolve all parameters for AppComponent:

How should this be done correctly?

UPDATED MODULE

const moment = new OpaqueToken('moment');

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [{provide: moment, useValue: moment}],
  bootstrap: [AppComponent]
})

export class AppModule {
}

UPDATED COMPONENT

import { Component } from '@angular/core';
import * as moment from 'moment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})


export class AppComponent {
  title = 'app works!';

  constructor(private moment: moment) {
    this.title += this.moment()
  }
}

There is an error on this line constructor(private moment: moment) which tells that: Cannot find name 'moment'.

Upvotes: 4

Views: 2410

Answers (6)

Kamalpreet
Kamalpreet

Reputation: 1143

Not sure if this helps now but you need something similar (not tested) to the following to make it work in your service.

import { Inject } from '@angular/core';

constructor(@Inject(moment) private moment) {
    this.title += this.moment()
  }

The key bit is the @Inject

Upvotes: 0

Merott
Merott

Reputation: 7379

The best workaround that I have come to find is to create a wrapper service, but also expose some of the most common methods directly on the service itself using getters:

import { Injectable } from '@angular/core';
import * as moment from 'moment';

/**
 * A wrapper for the moment library
 */
@Injectable()
export class MomentService {
  /**
   * Creates and returns a new moment object with the current date/time
   */
  public moment() { return moment(); }

  // expose moment properties directly on the service
  public get utc() { return moment.utc; }
  public get version() { return moment.version; }
  public get unix() { return moment.unix; }
  public get isMoment() { return moment.isMoment; }
  public get isDate() { return moment.isDate; }
  public get isDuration() { return moment.isDuration; }
  public get now() { return moment.now; }
}

Upvotes: 0

John Siu
John Siu

Reputation: 5092

Moment by itself is not an injectable for Angular2. However it can be wrapped inside one.

Plunker Demo

moment.service.ts

import { Injectable } from '@angular/core';
import * as m from 'moment';
@Injectable()
export class MomentService {
    moment = m;
}

app.module.ts

import { MomentService } from './moment.service';

@NgModule({
    providers: [MomentService]
    ...

app.component.ts

import { MomentService } from './moment.service';

export class AppComponent {
    constructor(private ms: MomentService){
        console.log('Moment:' + this.ms.moment("20111031", "YYYYMMDD").toString());
    }
}

Not perfect, but works.

Upvotes: 4

JayChase
JayChase

Reputation: 11525

If you are loading moment.js so it is available globally how about wrapping it with a service which you can then inject throughout your app?

import {Moment} from '../../node_modules/moment';

import { Injectable } from '@angular/core';

declare var moment: any;

@Injectable()
export class MomentService {
    constructor() { }

    get(): Moment {
        return moment;
    }
}

I this way you get the TS IntelliSense when you are coding (at least I do in VSCode) and you can handle mocking moment for testing easily too.

Upvotes: 0

Pankaj Parkar
Pankaj Parkar

Reputation: 136174

You need to use OpaqueToken which will allowed you to create a string based token. I'd just suggest you to change the name of moment to avoid thr collision with moment variable of library.

// You can place this is some file, so that you can export it.
export const Moment = new OpaqueToken('moment');

and then you could use

providers: [{provide: MomentStatic, useClass: moment}],

You can go through this article for more details

While using dependency include it in a Component constructor.

constructor(private moment: MomentStatic)

Upvotes: 3

Alexander Ciesielski
Alexander Ciesielski

Reputation: 10834

Moment doesn't have to be injected, it is a library that you can "just use". It's enough to import it in your typescript file now you can use moment's features.

Upvotes: 0

Related Questions