Search code examples
angulardatepickerdate-rangematerialdatepicker

How to remove disabled years from material datepicker?


For material datepicker we can use propery max and min to set range of avaliable years. But, it still display some unavaliable years. Is there any way to remove those years ? Need to remove years from 98-09


Solution

  • Bit of a hack, but since this is all HTML what you can do is create a custom directive to remove those items from your HTML.

    Create a directive:

    import {
      AfterViewInit,
      Directive,
      ElementRef,
      Renderer2
    } from '@angular/core';
    import { MatCalendarView, MatDatepicker } from '@angular/material/datepicker';
    
    @Directive({
      selector: '[disabledDates]',
    })
    export class DisabledDates implements AfterViewInit {
      constructor(
        private ref: ElementRef,
        private datePicker: MatDatepicker<any>,
        private rn: Renderer2
      ) {}
    
    
      ngAfterViewInit() {
        console.log('datepicker: ', this.datePicker);
    
        this.datePicker._viewChanged = this.onChangesForPicker.bind(this);
      }
    
      private onChangesForPicker(view: MatCalendarView) {
        console.log('Simple changes: ', view == 'multi-year');
    
        if (view == 'multi-year') {
          let items = document.getElementsByClassName(
            'mat-calendar-body-cell mat-calendar-body-disabled'
          );
    
          let arrayOfItems = Array.from(items);
          if (arrayOfItems && arrayOfItems.length)
            arrayOfItems.forEach((x) => {
              this.rn.removeChild(this.ref, x);
            });
        }
      }
    }
    

    What this does is taps into the private method of datePickers's _viewChanged. If the view is of the multi-year type then we know we are looking at our selection of available years.

    Then what it does is using the document it will get all the elements whose class name implies they are disabled. From there it calls the renderer and tells it to remove those children from the parent component. In this case the parent component is the datepicker.

    In your HTML you will use this directive by applying it to the mat-datepicker itself like so:

    <mat-datepicker #picker disabledDates></mat-datepicker>
    

    Working stackblitz:

    https://stackblitz.com/edit/angular-8dzujz?file=src/app/disabled-dates.directive.ts

    Edit: Directives in Angular are a powerful tool to help you create all sorts of custom behaviors, and ways to extend your code or tap into the underlying HTML of a separate component for which you have no direct access to (ex: Mat Datepicker)

    Edit 2:

    The .bind(this) is required because this in JS is a very odd and unfortunate creature. Without the binding, if you try to call elements which can be access using this inside of that method it will throw an undefined error because its version of this is different than the this that belongs to the directive itself. As such, binding it that way makes it so that any time we use this inside of that method (mind you intellisense will not complain if you don't do this) is the actual this of your directive.