Skip to content

Commit

Permalink
feat(locales): Added global locale configuration options
Browse files Browse the repository at this point in the history
Implemented provider `DateFnsConfigurationService` to work with global locale configurations

BREAKING CHANGE: * To import the module you have to use `DateFnsModule.forRoot()`.

re #135
  • Loading branch information
joanllenas committed Apr 8, 2018
1 parent ed1e0b1 commit 7f50125
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 59 deletions.
67 changes: 64 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ npm install --save date-fns
npm install --save ngx-date-fns
```

Usage
-----
Basic Usage
-----------

Import `DateFnsModule` into your app's modules:

Expand All @@ -32,7 +32,7 @@ import {DateFnsModule} from 'ngx-date-fns';
@NgModule({
imports: [
// (...)
DateFnsModule
DateFnsModule.forRoot()
]
})
```
Expand Down Expand Up @@ -86,6 +86,63 @@ Fri Jan 01 2016 00:00:00 GMT+0100 (CET)
alrededor de 1 mes
```

Working with locales
--------------------

> All locale aware pipes use English by default.
### Changing locale globally

Instead of passing the locale to each pipe via options you can set it globally in one single step by overriding the default `DateFnsConfiguration` implementation:

``` typescript
import { DateFnsModule } from 'ngx-date-fns';
import * as frLocale from "date-fns/locale/fr/index.js";

const frenchConfig = new DateFnsConfigurationService();
frenchConfig.setLocale(frLocale);

@NgModule({
imports: [
// (...)
DateFnsModule.forRoot()
],
providers: [
// (...)
{ provide: DateFnsConfigurationService, useValue: frenchConfig } // <---- All pipies in French by default
]
})
```

### Changing locale at runtime

It is also possible to change the default locale at runtime:

``` typescript
@Component({
selector: "app-root",
template: `
<p>
{{ dateOne | dfnsFormat : 'ddd MMM D YYYY' }}
</p>
<hr>
Set default locale to:
<a href="#" (click)="changeToGerman()">German</a>,
<a href="#" (click)="changeToSpanish()">Spanish</a>.
`
})
export class AppComponent {
dateOne = new Date(2016, 0, 1);
constructor(public config: DateFnsConfigurationService) {}
changeToGerman() {
this.config.setLocale(deLocale);
}
changeToSpanish() {
this.config.setLocale(esLocale);
}
}
```

Available pipes
---------------
#### Distance
Expand Down Expand Up @@ -192,3 +249,7 @@ Available pipes
* [dfnsLastDayOfQuarter](https://date-fns.org/docs/lastDayOfQuarter)
* [dfnsLastDayOfYear](https://date-fns.org/docs/lastDayOfYear)
* [dfnsLastDayOfISOYear](https://date-fns.org/docs/lastDayOfISOYear)

---

> *Library created with [ng-lib-schematics](https://github.com/caroso1222/ng-lib-schematics)*
35 changes: 26 additions & 9 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
import { Component } from '@angular/core';
import * as esLocale from 'date-fns/locale/es/index.js';
import { Component } from "@angular/core";
import * as esLocale from "date-fns/locale/es/index.js";
import * as deLocale from "date-fns/locale/de/index.js";
import { DateFnsConfigurationService } from "../lib/src/date-fns-configuration.service";

@Component({
selector: 'app-root',
selector: "app-root",
template: `
<p>
{{ dateOne | dfnsFormat : 'YYYY/MM/DD' }}
</p>
<p>
{{ [dateOne, dateTwo] | dfnsMin }}
{{ [dateOne, dateTwo] | dfnsMin | dfnsFormat : 'ddd MMM D YYYY' }}
</p>
<p>
{{ [dateOne, dateTwo] | dfnsMax | dfnsFormat : 'YYYY/MM/DD' }}
{{ [dateOne, dateTwo] | dfnsMax | dfnsFormat : 'ddd MMM D YYYY' }}
</p>
<p>
{{ dateThree | dfnsDistanceInWordsToNow : options }}
{{ dateThree | dfnsDistanceInWordsToNow : options }} - (Explicit 'es' locale)
</p>
<hr>
Set default locale to: <a href="#" (click)="changeToGerman()">German</a>, <a href="#" (click)="changeToSpanish()">Spanish</a>.
<p *ngFor="let d of dates">
{{ d | dfnsDistanceInWordsToNow }}
</p>
`
})
export class AppComponent {
dateOne = new Date(2016, 0, 1);
dateTwo = new Date(2017, 0, 1);
dateThree;
dateThree: Date;
dates: Date[];
options = {
locale: esLocale
}
constructor() {
};
constructor(public config: DateFnsConfigurationService) {
this.dateThree = new Date();
this.dateThree.setDate(-6);
this.dates = new Array(6)
.fill(new Date())
.map((d, i) => d.setDate(d.getDate() - Math.pow(5, i) ));
}
changeToGerman() {
this.config.setLocale(deLocale);
}
changeToSpanish() {
this.config.setLocale(esLocale);
}
}
8 changes: 6 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import { NgModule } from '@angular/core';

import { DateFnsModule } from '../lib';
import { AppComponent } from './app.component';
import { DateFnsConfigurationService } from '../lib/src/date-fns-configuration.service';
import * as frLocale from "date-fns/locale/fr/index.js";

const frenchConfig = new DateFnsConfigurationService();
frenchConfig.setLocale(frLocale);

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
DateFnsModule
DateFnsModule.forRoot()
],
providers: [],
providers: [ { provide: DateFnsConfigurationService, useValue: frenchConfig } ],
bootstrap: [AppComponent]
})
export class AppModule { }
63 changes: 63 additions & 0 deletions src/lib/src/date-fns-configuration.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component } from "@angular/core";
import { TestBed, ComponentFixture, async } from "@angular/core/testing";
import { DateFnsConfigurationService } from "./date-fns-configuration.service";
import { DateFnsModule } from ".";
import * as esLocale from "date-fns/locale/es/index.js";
import * as frLocale from "date-fns/locale/fr/index.js";

@Component({
template: `
<p class="dfns">{{ date | dfnsFormat : 'ddd MMM D YYYY' }}</p>
<p class="explicit">{{ date | dfnsFormat : 'ddd MMM D YYYY' : options }}</p>
`
})
class TestHostComponent {
date: Date = new Date();
options = { locale: frLocale };
}

describe('DateFnsConfigurationService', () => {

const mockConfig = new DateFnsConfigurationService();

beforeEach(async (() => {
TestBed.configureTestingModule({
declarations: [ TestHostComponent ],
imports: [ DateFnsModule.forRoot() ],
providers: [ {provide: DateFnsConfigurationService, useValue: mockConfig} ]
}).compileComponents();
}));

let component: TestHostComponent;
let fixture: ComponentFixture<TestHostComponent>;
let element: HTMLElement;

beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should display English date by default', () => {
component.date = new Date(2017, 11, 31);
element = fixture.nativeElement.querySelector('.dfns');
fixture.detectChanges();
expect(element.textContent).toBe('Sun Dec 31 2017');
});

it('should display Spanish date when changing config locale to Spanish', () => {
mockConfig.setLocale(esLocale);
component.date = new Date(2017, 11, 31);
element = fixture.nativeElement.querySelector('.dfns');
fixture.detectChanges();
expect(element.textContent).toBe('dom dic 31 2017');
});

it('should display French date even when changing config locale to Spanish if locale has been explicitly set via options', () => {
mockConfig.setLocale(esLocale);
component.date = new Date(2017, 11, 31);
element = fixture.nativeElement.querySelector('.explicit');
fixture.detectChanges();
expect(element.textContent).toBe('dim. déc. 31 2017');
});
});
50 changes: 50 additions & 0 deletions src/lib/src/date-fns-configuration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Injectable } from "@angular/core";

export interface DateFnsConfiguration {
/**
* Returns the default locale used by date-fns
*/
locale(): Object;

/**
* Sets the default locale used by date-fns
*/
setLocale(locale: Object): void;
}

@Injectable()
export class DateFnsConfigurationService implements DateFnsConfiguration {
private _locale: Object;

locale(): Object {
return this._locale;
}

setLocale(locale: Object): void {
this._locale = locale;
}
}

/**
* Helper function used by all pipes to calculate locale
*/
export const calculateLocale = (
options: { locale?: Object },
config: DateFnsConfiguration
): Object => {

const configLocale = config.locale();

if(!options && configLocale) {
return {locale: configLocale};
}

if(options && !options.locale && configLocale) {
return {
...options,
locale: configLocale
};
}

return options;
};
12 changes: 10 additions & 2 deletions src/lib/src/date-fns.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NgModule } from '@angular/core';
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';

import { DistanceInWordsPipe } from './distance-in-words.pipe';
Expand Down Expand Up @@ -95,6 +95,7 @@ import { DifferenceInCalendarISOYearsPipe } from './difference-in-calendar-iso-y
import { DifferenceInISOYearsPipe } from './difference-in-iso-years.pipe';
import { GetQuarterPipe } from './get-quarter.pipe';
import { GetISOYearPipe } from './get-iso-year.pipe';
import { DateFnsConfigurationService } from './date-fns-configuration.service';


const PIPES = [
Expand Down Expand Up @@ -218,4 +219,11 @@ const PIPES = [
declarations: PIPES,
exports: PIPES
})
export class DateFnsModule { }
export class DateFnsModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: DateFnsModule,
providers: [DateFnsConfigurationService]
}
}
}
3 changes: 2 additions & 1 deletion src/lib/src/distance-in-words-strict.pipe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

import { DistanceInWordsStrictPipe } from './distance-in-words-strict.pipe';
import * as esLocale from 'date-fns/locale/es/index.js';
import { DateFnsConfigurationService } from './date-fns-configuration.service';

describe('DistanceInWordsStrictPipe', () => {
let pipe: DistanceInWordsStrictPipe;

beforeEach(() => pipe = new DistanceInWordsStrictPipe());
beforeEach(() => pipe = new DistanceInWordsStrictPipe(new DateFnsConfigurationService()));

it('should throw when required arguments are not provided', () => {
expect(() => pipe.transform(undefined, undefined))
Expand Down
31 changes: 20 additions & 11 deletions src/lib/src/distance-in-words-strict.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import {Pipe, PipeTransform} from '@angular/core';
import {distanceInWordsStrict} from 'date-fns';
import { Pipe, PipeTransform } from "@angular/core";
import { distanceInWordsStrict } from "date-fns";
import {
DateFnsConfigurationService,
calculateLocale
} from "./date-fns-configuration.service";


@Pipe({ name: 'dfnsDistanceInWordsStrict' })
@Pipe({ name: "dfnsDistanceInWordsStrict", pure: false })
export class DistanceInWordsStrictPipe implements PipeTransform {
static readonly NO_ARGS_ERROR = 'dfnsDistanceInWordsStrict: missing required arguments';
static readonly NO_ARGS_ERROR = "dfnsDistanceInWordsStrict: missing required arguments";

constructor(public config: DateFnsConfigurationService) {}

transform(
dateToCompare: string | number | Date,
date: string | number | Date,
options?: {
addSuffix?: boolean,
unit?: 's' | 'm' | 'h' | 'd' | 'M' | 'Y',
partialMethod?: 'floor' | 'ceil' | 'round',
locale?: Object
addSuffix?: boolean;
unit?: "s" | "m" | "h" | "d" | "M" | "Y";
partialMethod?: "floor" | "ceil" | "round";
locale?: Object;
}
): string {
if (!dateToCompare || !date) {
throw new Error(DistanceInWordsStrictPipe.NO_ARGS_ERROR);
throw new Error(DistanceInWordsStrictPipe.NO_ARGS_ERROR);
}
return distanceInWordsStrict(dateToCompare, date, options);
return distanceInWordsStrict(
dateToCompare,
date,
calculateLocale(options, this.config)
);
}
}
3 changes: 2 additions & 1 deletion src/lib/src/distance-in-words-to-now.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { DistanceInWordsToNowPipe } from './distance-in-words-to-now.pipe';
import * as esLocale from 'date-fns/locale/es/index.js';
import { DateFnsConfigurationService } from './date-fns-configuration.service';

describe('DistanceInWordsToNowPipe', () => {
let pipe: DistanceInWordsToNowPipe;

beforeEach(() => {
pipe = new DistanceInWordsToNowPipe();
pipe = new DistanceInWordsToNowPipe(new DateFnsConfigurationService());
jasmine.clock().install();
jasmine.clock().mockDate(new Date(2015, 0, 1));
});
Expand Down
Loading

0 comments on commit 7f50125

Please sign in to comment.