Angular Material Datepicker with Range Selection

爷,独闯天下 提交于 2020-02-25 04:37:05

问题


I'm using angular 8 and I wanted implement a datepicker with daterage highlighted: enter image description here I was looking for some package like this one: https://www.npmjs.com/package/saturn-datepicker Unfortunately it has been deprecated and currently I cannot find others like that. Do you know something for my purpose? Thank you.


回答1:


IMPORTANT UPDATE, My good!! google is plenty full of controls like mine! (remember: Google is my friend, google is my friend...)

I'm making a datePicker range in this stackblitz, i'll try to explain after

Update I improve the stackblitz and a brief explanation

There are a point important with the mat-date-picker that it's not possible create a day-template. So it's necesary use Renderer2 to add listener, add classes or remove classes. I need use document.querySelectorAll as it's show in this another SO

The idea is, with the cells selected create an array of object

{
   date: //will be a getTime of the date,
   element: //will be the nativeElement x,
   change: //a boolean to indicate this cell has an aditional class
   x.listen: //the listener added to allow us remove the listener
}

And a function who help us to add/remove class acording two variables: this._dateTo and another variable that will be this._dateFrom or another one (early we discovered why)

  redrawCells(timeTo: number) {
    timeTo = timeTo || this._dateTo;
    if (timeTo<this._dateFrom)
      timeTo=this._dateFrom
    this.cells.forEach(x => {
      const change = x.date >= this._dateFrom && x.date <= timeTo;
      if (change || x.change) {
        x.change = change;
        const addInside = x.change ? "addClass" : "removeClass";
        const addFrom = x.date == this._dateFrom? "addClass":
                        x.date == timeTo && this._dateFrom==timeTo? "addClass":
                        "removeClass";
        const addTo = x.date == timeTo? "addClass": 
                      x.date == this._dateFrom && this._dateFrom==timeTo? "addClass":
                        "removeClass";

        this.renderer[addInside](x.element, "inside");
        this.renderer[addFrom](x.element, "from");
        this.renderer[addTo](x.element, "to");
      }
    });
  }

A new function setCell will be who manage the td to add/remove listener of mouse over. It's necesary enclosed in a setTimeout because we call this function before the calendar is drawed

  setCells() {
    setTimeout(() => {
      if (this.cells) {
        this.cells.forEach(x => {
          x.listen();  //<---remove the listener
        });
      }
      this.dateOver = null;
      //get the elements
      let elements = document.querySelectorAll(".calendar");
      if (!elements || elements.length == 0) return;
      const cells = elements[0].querySelectorAll(".mat-calendar-body-cell");
      this.cells = [];
      //with each element we fill our array "this.cells"
      cells.forEach((x, index) => {
        const date = new Date(x.getAttribute("aria-label"));
        const time=new Date(date.getFullYear() +"-" +(date.getMonth() + 1) +
              "-" +date.getDate()).getTime()

        this.cells.push({
          date: time,
          element: x,
          change:time>=this._dateFrom && time<=this._dateTo
        });
      });
      this.cells.forEach(x => {
        if (!x.listen) {
          //we add a listener "mouseover"
          x.listen = this.renderer.listen(x.element, "mouseover", () => {
            if (!this._dateTo && this.dateOver != x.date) {
              this.dateOver = x.date;
              this.redrawCells(this.dateOver); //who call to redrawCells
            }
          });
        }
      });
    });
  }

Well, the rest of function necesary to parse and format a date and open a mat-calendar using a mat-menu is from this another SO. In this last SO, the calendar not close, so, we can use to force two clicks to get a dateFrom and a dateTo

  select(date: any) {
    date = new Date(
      date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate()
    );
    if (
      !this.from.value ||
      (this.from.value && this.to.value) ||
      this._dateFrom > date.getTime()
    ) {
      this.dateFrom = date;
      this.dateTo = null;
      this.redrawCells(date.getTime());
    } else {
      this.dateTo = date;
      this.trigger.closeMenu();
    }
  }

I like create a mat custom component. This allow us use the component in a mat-form-field, some like

<mat-form-field class="full-width" >
  <mat-label>Select dates</mat-label>
  <date-picker-range [formControl]="control" placeholder="DD/MM/YYYY"  ></date-picker-range>
  <mat-error>required</mat-error>
</mat-form-field>

And allow e.g. us use [disabled] to disabled the formControl, and the label move like another mat-input. For this we need add some functions, see (I promise it's the last) this last recent SO

NOTE: The control is "AS IS" without any warranty and it's allowed: critics it, improve, modify, use or put it as bad example




回答2:


In material date picker you can define max and min values.

For example:

example.hml <input matInput [min]="minValue" [max]="maxValue" [matDatepicker]="picker" formControlName="name"/> <mat-datepicker #voltageToDatePiker [startAt]="tomorrow"></mat-datepicker>

example.ts minValue = new Date(); maxValue = new Date();



来源:https://stackoverflow.com/questions/60247962/angular-material-datepicker-with-range-selection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!