Primeng: range and multiple mode support for Month Picker

Created on 15 Jan 2019  路  5Comments  路  Source: primefaces/primeng

I'm submitting a ...

[x] bug report => Search github for a similar issue or PR before submitting
[ ] feature request => Please check if request is not on the roadmap already https://github.com/primefaces/primeng/wiki/Roadmap
[ ] support request => Please do not submit support request here, instead see http://forum.primefaces.org/viewforum.php?f=35

Plunkr Case (Bug Reports)
https://stackblitz.com/edit/github-shrafb?file=src%2Fapp%2Fapp.component.html

Current behavior
cannot choose date after first pick

isMonthSelected doesn't check if value is Array
https://github.com/primefaces/primeng/blob/2eee286dd973211d2c58d288bb62af598cee331b/src/app/components/calendar/calendar.ts#L987

  • Angular version: 7.0

  • PrimeNG version: 7.0.4

new feature

All 5 comments

Hi @user753 ,
I don麓t know if you still need it, but I found a temporary solution for month range, I took the original date range functions and I adapted them.

In your template:

 <p-calendar  [(ngModel)]="dateFilters" #monthPicker appendTo="body" selectionMode="range" [readonlyInput]="true" dateFormat="mm/yy" [showButtonBar]="true"></p-calendar>

In your component:

....

export class SomeComponent implements OnInit, AfterViewInit {

dateFilters: any;
@ViewChild('monthPicker') private _monthPicker: any;
readonly dateNow: Date = new Date(Date.now());

  ngOnInit() { }

  ngAfterViewInit(): void {
    const currentYear = this.dateNow.getFullYear();
    const currentMonth = this.dateNow.getMonth();

// I used it inside ngAfterViewInit, because I used it like a filter in TurboTable, but you can try it in ngOnInit
// Override the "isMonthSelected" function
    this._monthPicker.isMonthSelected = (month) => this.isMonthSelectedHandler(month,
      this.dateFilters, currentMonth, currentYear);
  }

isMonthSelectedHandler(month: number, value: any, currentMonth: number, currentYear: number) {
        const dateMeta = { month: currentMonth, year: currentYear};
        if (value) {
            if (value[1]) {
                return this.monthEqualsHandler(value[0], month, currentYear) ||
                    this.monthEqualsHandler(value[1], month, currentYear) ||
                    this.isMonthBetweenHandler(value[0], value[1], dateMeta);
            } else {
                return this.monthEqualsHandler(value[0], month, currentYear);
            }
        } else {
            return false;
        }
    }

monthEqualsHandler(value: any, month: number, currentYear: number) {
        return value ? (value.getMonth() === month && value.getFullYear() === currentYear) : false;
    }

isMonthBetweenHandler(start, end, dateMeta) {
        let between: boolean = false;
        if (start && end) {
            const date: Date = new Date(dateMeta.year, dateMeta.month, 1);
            return start.getMonth() <= date.getMonth() && start.getFullYear() <= date.getFullYear() &&
                end.getMonth() >= date.getMonth() && end.getFullYear() >= date.getFullYear();
        }

        return between;
    }

}

Hope it helps!

Regards.

Thank you, @jenniferarce , your solution is works, but has some design issues in default theme.
But I think this must be "out of the box" implementation of this function.

PrimeNG 8.0.0-RC1 still has this issue.

A little improved solution without design issues, working from any to any monthes, but still not working if switching through years.
Adapted for PrimeNG 8.0.0-RC1

HTML:

<p-calendar #dateFilter (onSelect)="onDatesRangeFilterSelected($event)" [(ngModel)]="datesRangeFilter" selectionMode="range" view="month" dateFormat="mm/yy" [readonlyInput]="true"></p-calendar>

TypeScript:

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

@ViewChild('dateFilter', undefined) private dateFilter: any;
private datesRangeFilterCurrentSelectedValue: Date;
public datesRangeFilter: Date[];

ngAfterViewInit(): void {
    this.dateFilter.isMonthSelected = (currentHandlingMonth) => this.isMonthSelectedHandler(currentHandlingMonth, this.datesRangeFilterCurrentSelectedValue, this.datesRangeFilter);
}

public onDatesRangeFilterSelected(selectedValue: Date) {
    this.datesRangeFilterCurrentSelectedValue = selectedValue;
}

private isMonthSelectedHandler(currentHandlingMonth: number, selectedDate: Date, datesRange: Date[]) {
    if (selectedDate) {
        let selectedDateFullYear = selectedDate.getFullYear();
        if (datesRange) {
            if (datesRange[1]) {
                return this.monthEqualsHandler(datesRange[0], currentHandlingMonth, selectedDateFullYear) ||
                    this.monthEqualsHandler(datesRange[1], currentHandlingMonth, selectedDateFullYear) ||
                    this.isMonthBetweenHandler(datesRange[0], datesRange[1], selectedDate, currentHandlingMonth);
            } else {
                return this.monthEqualsHandler(datesRange[0], currentHandlingMonth, selectedDateFullYear);
            }
        } else {
            return false;
        }
    }
}

private monthEqualsHandler(value: Date, month: number, year: number) {
    return value ? (value.getMonth() === month && value.getFullYear() === year) : false;
}

private isMonthBetweenHandler(start: Date, end: Date, selectedDate: Date, currentHandlingMonth: number) {
    let between: boolean = false;
    if (start && end) {
        return start.getMonth() <= currentHandlingMonth && start.getFullYear() <= selectedDate.getFullYear() &&
            end.getMonth() >= currentHandlingMonth && end.getFullYear() >= selectedDate.getFullYear();
    }
    return between;
}

Hi @PicOLinO , thanks for your solution, added to what you have done, I found the solution for the year change. I did some changes only on Typescript (I will copy your html code, just to keep it complete), because I realize that the 'ui-state-active' class is added according to isMonthSelect (you can check it here https://github.com/primefaces/primeng/blob/master/src/app/components/calendar/calendar.ts).

PrimeNG version: 7.1.0

HTML (your solution):

<p-calendar #dateFilter (onSelect)="onDatesRangeFilterSelected($event)" [(ngModel)]="datesRangeFilter" selectionMode="range" view="month" dateFormat="mm/yy" [readonlyInput]="true"></p-calendar>

Typescript:

import { ViewChild } from '@angular/core';
import { Calendar } from 'primeng/calendar';

@ViewChild('dateFilter', undefined) private dateFilter: Calendar;
private datesRangeFilterCurrentSelectedValue: Date;
public datesRangeFilter: Date[];

  ngAfterViewInit(): void {

    this.dateFilter.isMonthSelected = (currentHandlingMonth) =>   
 this.isMonthSelectedHandler(currentHandlingMonth, this.datesRangeFilterCurrentSelectedValue, this.dateFilters, this.dateFilter.currentYear);

  }

public onDatesRangeFilterSelected(selectedValue: Date) {
    this.datesRangeFilterCurrentSelectedValue = selectedValue;
}

    private isMonthSelectedHandler(currentHandlingMonth: number, selectedDate: Date, datesRange: Date[], currentYear: number) {

        if (selectedDate) {

            if (datesRange) {
                if (datesRange[1]) {
                    return this.monthEqualsHandler(datesRange[0], currentHandlingMonth, currentYear) ||
                        this.monthEqualsHandler(datesRange[1], currentHandlingMonth, currentYear) ||
                        this.isMonthBetweenHandler(datesRange[0], datesRange[1], currentHandlingMonth, currentYear);
                } else {
                    return this.monthEqualsHandler(datesRange[0], currentHandlingMonth, currentYear);
                }
            } else {
                return false;
            }
        }
    }

  private monthEqualsHandler(value: any, currentHandlingMonth: number, currentHandlingYear: number) {
        return value ? (value.getMonth() === currentHandlingMonth && value.getFullYear() === currentHandlingYear) : false;
    }


 private isMonthBetweenHandler(start: Date, end: Date, currentHandlingMonth: number, currentHandlingYear: number) {
        let between: boolean = false;
        if (start && end) {

            const startDate = new Date(start.getFullYear(), start.getMonth(), 1);
            const endDate = new Date(end.getFullYear(), end.getMonth(), 1);
            const currentDate = new Date(currentHandlingYear, currentHandlingMonth, 1);

            // OPT 01
            between = startDate.getTime() <= currentDate.getTime() &&
                endDate.getTime() >= currentDate.getTime();

            // OPT 02: If you don't want to manage days, this another option its working:
             // between = (currentHandlingMonth > start.getMonth() && currentHandlingYear >= start.getFullYear()
            //     && ((currentHandlingMonth <= end.getMonth() && currentHandlingYear <= end.getFullYear())
            //         || currentHandlingMonth > end.getMonth() && currentHandlingYear < end.getFullYear()))
            //     || (currentHandlingMonth <= start.getMonth() && currentHandlingYear > start.getFullYear()
            //         && currentHandlingMonth < end.getMonth() && currentHandlingYear <= end.getFullYear());
        }

        return between;
    }

Hope it helps!

Regards.

Thank you @jenniferarce , your solution works like a charm!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

markgoho picture markgoho  路  3Comments

pchristou picture pchristou  路  3Comments

gatapia picture gatapia  路  3Comments

mitosandov picture mitosandov  路  3Comments

jisqaqov picture jisqaqov  路  3Comments