453

In AngularJS, I am able to use filters (pipes) inside of services and controllers using syntax similar to this:

$filter('date')(myDate, 'yyyy-MM-dd');

Is it possible to use pipes in services/components like this in Angular?

Lazar Ljubenović
  • 18,976
  • 10
  • 56
  • 91
POSIX-compliant
  • 4,653
  • 2
  • 11
  • 12

9 Answers9

855

As usual in Angular, you can rely on dependency injection:

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

class MyService {

  constructor(private datePipe: DatePipe) {}

  transformDate(date) {
    return this.datePipe.transform(date, 'yyyy-MM-dd');
  }
}

Add DatePipe to your providers list in your module; if you forget to do this you'll get an error no provider for DatePipe:

providers: [DatePipe,...]

Update Angular 6: Angular 6 now offers pretty much every formatting functions used by the pipes publicly. For example, you can now use the formatDate function directly.

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

class MyService {

  constructor(@Inject(LOCALE_ID) private locale: string) {}

  transformDate(date) {
    return formatDate(date, 'yyyy-MM-dd', this.locale);
  }
}

Before Angular 5: Be warned though that the DatePipe was relying on the Intl API until version 5, which is not supported by all browsers (check the compatibility table).

If you're using older Angular versions, you should add the Intl polyfill to your project to avoid any problem. See this related question for a more detailed answer.

cexbrayat
  • 17,772
  • 4
  • 27
  • 22
  • What would be the result of using DatePipe in a browser that doesn't support Intl? Is there any type of shim/ployfill available to counter lack of support? – POSIX-compliant Feb 02 '16 at 17:35
  • It will sadly throw an error and break your app right now. There are issues opened on the Github tracker, but it looks like there is currently no good polyfill... – cexbrayat Feb 02 '16 at 17:59
  • 5
    This doesn't appear to work for custom pipes which themselves use dependency injection in their constructor. Or am I mistaken? – Murray Rowan Mar 09 '16 at 01:29
  • 1
    Even with Angular 6+, I prefer using the DatePipe over formatDate, as it will automatically apply the current LOCALE_ID, whereas formatDate appears to require the local be passed in which requires injecting it into the constructor as shown in the example. – reads0520 Nov 26 '18 at 20:54
91

This answer is now outdated

recommend using DI approach from other answers instead of this approach

Original answer:

You should be able to use the class directly

new DatePipe().transform(myDate, 'yyyy-MM-dd');

For instance

var raw = new Date(2015, 1, 12);
var formatted = new DatePipe().transform(raw, 'yyyy-MM-dd');
expect(formatted).toEqual('2015-02-12');
C.OG
  • 6,236
  • 3
  • 20
  • 38
SnareChops
  • 13,175
  • 9
  • 69
  • 91
  • Can you explain why the month comes out as 2 instead of 1? Also, it looks like you are missing a 'y' in the format passed to transform. – POSIX-compliant Feb 02 '16 at 04:33
  • 2
    When using the javascript `Date` constructor, months are `0` based. So `0` is January and `1` is February. Corrected missing `y` – SnareChops Feb 02 '16 at 04:34
  • 24
    Incase it helps any one else, date pipe is imported from 'angular2/common'. – POSIX-compliant Feb 02 '16 at 04:39
  • 1
    Code snippet doesn't compile.... `error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'string'.` on line `var formatted = new DatePipe().transform(raw, ['yyyy-MM-dd']);` – Paul Gorbas Jul 01 '16 at 23:10
  • @PaulGorbas This is probably a change in the API from the beta to the rc version of Angular2. Most likely they changed the array argument to a spread. In that case change remove the `[]` from the second argument and recompile. – SnareChops Jul 02 '16 at 13:22
  • 10
    Now released Angular v2.0.0, and you can inject this pipe. First, add to NgModule: `@NgModule({ providers:[DatePipe] })`, then in your class, import and inject `constructor( private datePipe: DatePipe ){}` – ktretyak Sep 28 '16 at 00:27
  • 2
    meanwhile the Angular2 DatePipe expects the Locale_ID as constructor argument. So if you try to use it directly you would have to provide a fix Locale_ID and therefor it won't take apps Locale_ID anymore. That why I would NOT recommend to go that way. – E. Hein Feb 17 '17 at 10:35
  • 1
    @E.Hein I've updated the answer to alert people that this is not the optimal approach. This answer was posted a long time ago before rc.4 even. I agree that sacrificing the locale detection is bad. Thanks for bringing this change up. – SnareChops Feb 17 '17 at 19:08
  • If you would like to use a Pipe in Angular 2/4, you will have to declare the Pipe under the `declarations` in @NgModule, like so: `@NgModule({declarations: [MyCustomPipe]})` – Devner Jul 12 '17 at 10:40
  • Thank you to authors of all answers clear and consice, saved me time! – vicgoyso Jun 23 '18 at 20:56
  • 1
    What do you mean with "Using DI approach"? Using the "new" keyword has nothing to do with DI anymore. – Marco Klein Nov 26 '18 at 16:21
  • 1
    @MarcoKlein You are correct. The post is just unclear. I was attempting to recommend others to use the DI approach instead of following my answer. I will edit to make this more clear. – SnareChops Nov 29 '18 at 23:34
32

Yes, it is possible by using a simple custom pipe. Advantage of using custom pipe is if we need to update the date format in future, we can go and update a single file.

import { Pipe, PipeTransform } from '@angular/core';
import { DatePipe } from '@angular/common';

@Pipe({
    name: 'dateFormatPipe',
})
export class dateFormatPipe implements PipeTransform {
  transform(value: string) {
    const datePipe = new DatePipe("en-US");
    value = datePipe.transform(value, 'MMM-dd-yyyy');

    return value;
  }
}
{{currentDate | dateFormatPipe }}

You can always use this pipe anywhere , component, services etc.

For example:

import { Component } from '@angular/core';
import {dateFormatPipe} from './pipes'

export class AppComponent {
  currentDate : any;
  newDate : any;

  constructor() {
    this.currentDate = new Date().getTime();
    let dateFormatPipeFilter = new dateFormatPipe();
    this.newDate = dateFormatPipeFilter.transform(this.currentDate);

    console.log(this.newDate);
}
ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
Prashobh
  • 9,216
  • 15
  • 61
  • 91
23

Other answers don't work in angular 5?

I got an error because DatePipe is not a provider, so it cannot be injected. One solution is to put it as a provider in your app module but my preferred solution was to instantiate it.

Instantiate it where needed:

I looked at DatePipe's source code to see how it got the locale: https://github.com/angular/angular/blob/5.2.5/packages/common/src/pipes/date_pipe.ts#L15-L174

I wanted to use it within a pipe, so my example is within another pipe:

    import { Pipe, PipeTransform, Inject, LOCALE_ID } from '@angular/core';
    import { DatePipe } from '@angular/common';

    @Pipe({
        name: 'when',
    })
    export class WhenPipe implements PipeTransform {
        static today = new Date((new Date).toDateString().split(' ').slice(1).join(' '));
        datePipe: DatePipe;

        constructor(@Inject(LOCALE_ID) private locale: string) {
            this.datePipe = new DatePipe(locale);
        }
        transform(value: string | Date): string {
            if (typeof(value) === 'string')
                value = new Date(value);

            return this.datePipe.transform(value, value < WhenPipe.today ? 'MMM d': 'shortTime')
        }
    }

The key here is importing Inject, and LOCALE_ID from angular's core, and then injecting that so you can give it to the DatePipe to instantiate it properly.

Make DatePipe a provider

In your app module you could also add DatePipe to your providers array like this:

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

    @NgModule({
        providers: [
            DatePipe
        ]
    })

Now you can just have it injected in your constructor where needed (like in cexbrayat's answer).

Summary:

Either solution worked, I don't know which one angular would consider most "correct" but I chose to instantiate it manually since angular didn't provide datepipe as a provider itself.

raaaay
  • 496
  • 7
  • 14
csga5000
  • 4,062
  • 4
  • 39
  • 52
  • 3
    You can make it also a per component provider – Jimmy Kane Apr 16 '18 at 12:07
  • Thanks, your answer is the most exhaustive. I am looking for some resources on differences between instantiating the pipe with new or dependency injecting it directly and adding it to providers and can't find anything. I prefer the 2nd approach, cause when you `new`up the pipe, you still have to DI the locale. I find the whole `@Inject(LOCALE_ID) private locale: string` syntax cumbersome. – codeepic Dec 04 '18 at 10:16
  • @codeepic I probably wouldn't say there is a huge difference really. If you ask me, angular probably should have made it a provider. – csga5000 Dec 06 '18 at 22:10
22

If you don't want to do new myPipe() because you're injecting dependencies to pipe, you can inject in component like provider and use without new.

Example:

import { Component, OnInit } from '@angular/core';
import { myPipe} from './pipes';

@Component({
  selector: 'my-component',
  template: '{{ data }}',
  providers: [ myPipe ]
})
export class MyComponent() implements OnInit {
  data = 'some data';

  constructor(private myPipe: myPipe) {}

  ngOnInit() {
    this.data = this.myPipe.transform(this.data);
  }
}
ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
andy
  • 2,362
  • 2
  • 18
  • 19
  • Nice one But I think it only answers half of the question because all the answers here talk about pipes with services and didn't mention the provided pipes by angular that can be used with templates directly like {{ Welcome to Angular | lowercase}} etc you can mention it ;) – Rebai Ahmed Jul 13 '21 at 22:42
  • 1
    This answer was helpful but a provider also needed to be added to the module providers as mentioned in other answers: `@NgModule({providers: [DatePipe]})` – jbobbins Sep 14 '22 at 21:34
16

If you want to use your custom pipe in your components, you can add

@Injectable({
  providedIn: 'root'
})

annotation to your custom pipe. Then, you can use it as a service

srt
  • 171
  • 1
  • 5
  • is it good to have `providedIn: 'root'` inside our pipe or provided in a local module where pipe is used? – Daniel.V Feb 05 '19 at 09:37
  • 1
    It depends on where you use the pipe. If you use the pipe in only one module then you may select the second option. But if you use the pipe in several modules in your app, you should select the first option which is providedIn: 'root' – srt Feb 06 '19 at 10:16
11

As of Angular 6 you can import formatDate from @angular/common utility to use inside the components.

It was intruduced at https://github.com/smdunn/angular/commit/3adeb0d96344c15201f7f1a0fae7e533a408e4ae

I can be used as:

import {formatDate} from '@angular/common';
formatDate(new Date(), 'd MMM yy HH:mm', 'en');

Although the locale has to be supplied

Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
9

You can use formatDate() to format the date in services or component ts. syntax:-

    formatDate(value: string | number | Date, format: string, locale: string, timezone?: string): string

import the formatDate() from common module like this,

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

and just use it in the class like this ,

    formatDate(new Date(), 'MMMM dd yyyy', 'en');

You can also use the predefined format options provided by angular like this ,

    formatDate(new Date(), 'shortDate', 'en');

You can see all other predefined format options here ,

https://angular.io/api/common/DatePipe

raaaay
  • 496
  • 7
  • 14
Sksaif Uddin
  • 642
  • 1
  • 15
  • 22
1

I use this method:

 import { Component, OnInit } from '@angular/core';
    import {DatePipe} from '@angular/common';
    
    @Component({
      selector: 'my-app',
     templateUrl: './my-app.component.html',
      styleUrls: ['./my-app.component.scss']
    })
    export class MyComponent() implements OnInit {
    
      constructor(private datePipe: DatePipe) {}
    
      ngOnInit(): void {
        let date = this.transformDate('2023-02-01T06:40:49.562Z');
        console.log('date:',date);
      }
    
      transformDate(date: string) {
        return this.datePipe.transform(date, 'yyyy-MM-dd');
      }
    }
Fahimeh Ahmadi
  • 813
  • 8
  • 13