Reputation: 41
I need your help using NgbDateParserFormatter for the datepicker of ng-bootstrap. I need the german format dd.MM.yyyy (for example 31.03.2017).
I have got a plunker, but it does not work correctly:
Here is the code:
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { DatePipe } from '@angular/common';
export class CustomNgbDateParserFormatter extends NgbDateParserFormatter {
datePipe = new DatePipe('de-AT');
constructor(
private dateFormatString: string) {
super();
}
format(date: NgbDateStruct): string {
if (date === null) {
return '';
}
try {
return this.datePipe.transform(new Date(date.year, date.month - 1, date.day), this.dateFormatString);
} catch (e) {
return '';
}
}
parse(value: string): NgbDateStruct {
let returnVal: NgbDateStruct;
if (!value) {
returnVal = null;
} else {
try {
let dateParts = this.datePipe.transform(value, 'M-d-y').split('-');
returnVal = { year: parseInt(dateParts[2]), month: parseInt(dateParts[0]), day: parseInt(dateParts[1]) };
} catch (e) {
returnVal = null;
}
}
return returnVal;
}
}
Could you help me, please?
Thank you very much for your help.
Upvotes: 1
Views: 2154
Reputation: 11
Here is my solution I hope it will help you. To make it work you have to make an entry in app.module.ts like this:
providers: [...,
{ provide: NgbDateParserFormatter, useClass: NgbDateFRParserFormatter }]
You select a date from datepicker or enter the date manually, It will automatical converts its format to dd.MM.yyyy.
import { Injectable, Inject } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
@Injectable()
export class NgbDateFRParserFormatter extends NgbDateParserFormatter {
parse(value: string): NgbDateStruct {
if (value) {
const dateParts = value.trim().split('/');
if (dateParts.length === 1 && isNumber(dateParts[0])) {
return { year: toInteger(dateParts[0]), month: null, day: null };
} else if (
dateParts.length === 2 &&
isNumber(dateParts[0]) &&
isNumber(dateParts[1])
) {
return {
year: toInteger(dateParts[1]),
month: toInteger(dateParts[0]),
day: null
};
} else if (
dateParts.length === 3 &&
isNumber(dateParts[0]) &&
isNumber(dateParts[1]) &&
isNumber(dateParts[2])
) {
return {
year: toInteger(dateParts[2]),
month: toInteger(dateParts[1]),
day: toInteger(dateParts[0])
};
}
}
return null;
}
format(date: NgbDateStruct): string {
let stringDate = '';
const language = this.authService.getUser().language;
if (date) {
stringDate += isNumber(date.day) ? padNumber(date.day) + '.' : '';
stringDate += isNumber(date.month) ? padNumber(date.month) + '.' : '';
stringDate += date.year;
}
return stringDate;
}
}
function padNumber(value: number) {
if (isNumber(value)) {
return `0${value}`.slice(-2);
} else {
return '';
}
}
function isNumber(value: any): boolean {
return !isNaN(toInteger(value));
}
function toInteger(value: any): number {
return parseInt(`${value}`, 10);
}
Upvotes: 1
Reputation: 11
This is mostly pkozlowski.opensource answer but with swapped month/day since that's the common format in Germany.
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { DatePipe } from '@angular/common';
function padNumber(value: number) {
if (isNumber(value)) {
return `0${value}`.slice(-2);
} else {
return '';
}
}
function toInteger(value: any): number {
return parseInt(`${value}`, 10);
}
function isNumber(value: any): boolean {
return !isNaN(toInteger(value));
}
export class CustomNgbDateParserFormatter extends NgbDateParserFormatter {
format(date: NgbDateStruct): string {
return date ? `${padNumber(date.day)}.${padNumber(date.month)}.${date.year}` : '';
}
parse(value: string): NgbDateStruct {
if (value) {
const dateParts = value.trim().split('.');
if (dateParts.length === 1 && isNumber(dateParts[0])) {
return { year: null, day: toInteger(dateParts[0]), month: null };
} else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
return { year: null, day: toInteger(dateParts[0]), month: toInteger(dateParts[1]) };
} else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
return { year: toInteger(dateParts[2]), day: toInteger(dateParts[0]), month: toInteger(dateParts[1]) };
}
}
return null;
}
}
Upvotes: 1
Reputation: 117370
You are making your life harder for yourself by using JS Date
objects and the DatePipe
. Those are problematic for the following reasons:
Date
JS objects carry time and time-zone information so without being careful you can easily get into time-zone conversion related issues - this is precisely why ng-bootstrap is using dedicated data structuresDataPipe
uses Intl API under the hood and it is quite buggy, unfortunately.The good news is that you can easily implement desired functionality by following the ISO parser / formatter as seen here: https://github.com/ng-bootstrap/ng-bootstrap/blob/088363551b5d5ecd602a82aedfc97d43a75a080f/src/datepicker/ngb-date-parser-formatter.ts#L25-L45
If I'm reading your intention correctly, your code could look like follows:
export class CustomNgbDateParserFormatter extends NgbDateParserFormatter {
format(date: NgbDateStruct): string {
return date ? `${padNumber(date.month)}.${padNumber(date.day)}.${date.year}` : '';
}
parse(value: string): NgbDateStruct {
if (value) {
const dateParts = value.trim().split('.');
if (dateParts.length === 1 && isNumber(dateParts[0])) {
return {year: null, month: toInteger(dateParts[0]), day: null};
} else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
return {year: null, month: toInteger(dateParts[0]), day: toInteger(dateParts[1])};
} else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
return {year: toInteger(dateParts[2]), month: toInteger(dateParts[0]), day: toInteger(dateParts[1])};
}
}
return null;
}
}
Here is a plunker showing this in action: https://plnkr.co/edit/JdX0eHcl7QddOWxM9aT0?p=preview
Upvotes: 1