1

I have an array of days, which can be from any year. I'm trying to customize Angular Material datepicker to highlight some months in months selection view, and also some years in year selection view based on the array of days.

.html

<input [matDatepicker]="picker" />
<i class="fas fa-chevron-down"></i>
<mat-datepicker #picker></mat-datepicker>

.ts

arrayOfDays = [new Date('2019-01-01'), new Date('2020-08-10'),  new Date('2020-09-20')];

What I'm trying to achieve is to highlight 2019 and 2020 in year view, and if 2019 is selected, then highlight month JAN and if 2020 is selected, then highlight MAY & AUG in Red border except for current month and year like the below images.

month highlighting year highlighting

Is this possible?

I searched a lot and tried with dateClass, (yearSelected) event, etc but it is not applying for Months and Years.. If anyone know, how to do this kindly help..

FYI - https://stackblitz.com/edit/angular-date-class-filter-aactjv This is the one I found setting custom design to the dates view.. But i want similar implementation for Year and Months views.. Thanks in advance..

aks44
  • 422
  • 3
  • 12

1 Answers1

1

Really it's a bit complex control the material angular date picker events. I don't know if there're a better aproach, but you can get it using a custom header component, see this SO

Before, we are going to create a dataService

@Injectable({
  providedIn: "root"
})
export class DataService {
  arrayOfDays = [
    new Date("2019-01-01"),
    new Date("2020-08-10"),
    new Date("2020-09-20")
  ];

  constructor() {}
  displayMonth(year: number, view: string) {
    setTimeout(() => {
      let elements = document.querySelectorAll(".mat-calendar-content");
      let x = elements[0].querySelectorAll(".mat-calendar-body-cell-content");
      if (view == "multi-year") {
        const years = this.arrayOfDays.map(x => x.getFullYear());
        x.forEach((y: any) => {
          if (years.indexOf(+y.innerHTML) >= 0) {
            {
              y.style["border-color"] = "red";
            }
          }
        });
      }
      if (view=="year"){
        const monthsIni = [
          "JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"
        ];
        const months = this.arrayOfDays
          .filter(x => x.getFullYear() == year)
          .map(x => monthsIni[x.getMonth()]);
        x.forEach((y: any) => {
          if (months.indexOf(y.innerHTML) >= 0) {
            {
              y.style["border-color"] = "red";
            }
          }
        });
      }
    });
  }
}

After inject the dataService in the ExampleHeader and in the component, in the Example header is a fwe complex but it's only write

  constructor(_intl: MatDatepickerIntl,
              @Inject(forwardRef(() => MatCalendar)) calendar: MatCalendar<any>,
              @Optional() _dateAdapter: DateAdapter<any>,
              @Optional() @Inject(MAT_DATE_FORMATS) _dateFormats: MatDateFormats,
              changeDetectorRef: ChangeDetectorRef
              ,private dataService:DataService
              ) {
    super(_intl,calendar,_dateAdapter,_dateFormats,changeDetectorRef)
  }

When handles user clicks on the period label, user clicks on the previous button and user clicks on the next button to call the service

  currentPeriodClicked(): void {
    this.calendar.currentView =
      this.calendar.currentView == "month" ? "multi-year" : "month";
    this.dataService.displayMonth(
      this.calendar.activeDate.getFullYear(),
      this.calendar.currentView
    );
  }

  customPrev(): void {
    this.previousClicked();
    this.dataService.displayMonth(
      this.calendar.activeDate.getFullYear(),
      this.calendar.currentView
    );
  }

  customNext(): void {
    this.nextClicked();
    this.dataService.displayMonth(
      this.calendar.activeDate.getFullYear(),
      this.calendar.currentView
    );
  }

Futhermore we need make it also in yearSelected from the main component

  yearSelected(event: any) {
    this.dataService.displayMonth(
      event.getFullYear(),
      this.calendar.currentView
    );
  }

You can see in the stackblitz

Update if only want to "remark" the months, the thing is more simple, because we need only control the (yearSelected) event, so we can forget about the custom header.

<mat-datepicker #picker [dateClass]="dateClass"
 (yearSelected)="yearSelected($event)"  
 [calendarHeaderComponent]="exampleHeader">
</mat-datepicker>

months = [0,3] //<--in a variable the months we want remark

yearSelected(event:any)
  {
    setTimeout(()=>{
        let elements = document.querySelectorAll(".mat-calendar-content");
        let x = elements[0].querySelectorAll(".mat-calendar-body-cell-content");
        x.forEach((y: any,month:number) => {
          if (this.months.indexOf(month) >= 0) {
            {
              y.style["border-color"] = "red";
            }
          }
        });
      }
    )
  }

see stackblitz

Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Mine case is a little but similar but more simple. On Display of calendar ,how to highlight only months ? For example , June, July,August. – ahkeno Jul 22 '20 at 06:43
  • 1
    @ahkeno, sure it's more easy, you only need control the (yearSelected) event. futhermore, you can use index="yearSelected($event)", see the updated answer – Eliseo Jul 22 '20 at 07:03
  • @Eliseo thank for quick , efficient respond. But for now Highlight month can only view after click through header. I would like to display "highlight month on load". it is possible? – ahkeno Jul 22 '20 at 07:12
  • 1
    sure, you only need call to the function in `(opened)`. NOTE: put an "if" to check if calendar.view=="year", I just updated the stackblitz – Eliseo Jul 22 '20 at 07:23
  • @Eliseo thank so much! One more question, can able to make month range ? while I click on June, next two month July and August should selected ? – ahkeno Jul 22 '20 at 07:52
  • 1
    the stackblitz it's only how "mark" the months based in an array of months (of course you can change the months array and not use in "hard-code", e.g. you can has a variable month and create the array with this month and the two next one or has two variables fromMonth and toMonth and change the condition), but a range month it's more complex. Before material angular allow date-picker range, I wrote this SO:https://stackoverflow.com/questions/60247962/angular-material-datepicker-with-range-selection/60258340#60258340 -it's only a datepicker range for days- but can serve as inspiration – Eliseo Jul 22 '20 at 08:39