Указатель даты углового материала ограничивает выбор диапазона

#angular #datepicker #angular-material

#угловой #указатель даты #угловой-материал

Вопрос:

У меня есть угловой указатель даты материала для диапазонов (дата начала и дата окончания).

В настоящее время это свободный выбор. Это означает, что я могу выбрать любую начальную и любую конечную дату. Я бы хотел немного изменить его. Я хочу, чтобы он ограничивал разницу до 7 дней. Я не хочу разрешать пользователю выбирать 2 даты с разницей в днях более 7.

Итак, внутри календаря: это выглядит примерно так

Как вы можете видеть, 5 октября — это дата начала, и это позволяет нам выбрать 17 октября в качестве даты окончания. Но я хочу, чтобы пользователь мог выбирать конечную дату только в диапазоне от 5 октября до 12 октября (максимальная разница в 7 дней).

Есть ли способ?

Это мой HTML:

 <mat-form-field class="datepicker" appearance="fill">
    <mat-label>Enter a date range</mat-label>
    <mat-date-range-input [formGroup]="rangeForm" [rangePicker]="picker" [max]="maxDate">
        <input matStartDate formControlName="start" placeholder="Start date" readonly>
        <input matEndDate formControlName="end" placeholder="End date" readonly>
    </mat-date-range-input>
  

Typescript:

 import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import * as moment from 'moment';

@Component({
    selector: 'app-chart',
    templateUrl: './chart.component.html',
    styleUrls: ['./chart.component.scss']
})
export class ChartComponent implements OnInit {
    private _startDate: moment.Moment = moment().subtract(6, 'days');
    private _endData: moment.Moment = moment();
    
    public maxDate: Date = this._endData.toDate();
    public rangeForm: FormGroup = new FormGroup({
        start: new FormControl(this._startDate.toDate()),
        end: new FormControl(this._endData.toDate()),
    });
    
    constructor() { }
    
    ngOnInit(): void {
    }
    
    /**
    * Getter for start date
    * @returns string
    */
    public getStartDate(): string {
        return this._startDate.format('DD/MM/YYYY');
    }
    
    /**
    * Getter for end date
    * @returns string
    */
    public getEndDate(): string {
        return this._endData.format('DD/MM/YYYY');
    }
    
}
  

Комментарии:

1. Здесь, material.angular.io/components/datepicker/…

Ответ №1:

Реализация MatDateRangeSelectionStrategy,

Мы создаем поставщика, подобного

 @Injectable()
export class MaxRangeSelectionStrategy<D> implements MatDateRangeSelectionStrategy<D> {
    start: any;

    constructor(
        @Inject('rangeMax') private delta: number,
        private _dateAdapter: DateAdapter<D>
    ) {}

    selectionFinished(date: D, currentRange: DateRange<D>) {
        let { start, end } = currentRange;
        if (start == null || (start amp;amp; end)) {
            start = date;
            end = null;
        } else if (end == null) {
            const maxDate = this._dateAdapter.addCalendarDays(start, this.delta);
            end = date ? date > maxDate ? maxDate : date : null;
        }

        return new DateRange<D>(start, end);
    }

    createPreview(activeDate: D | null, currentRange: DateRange<D>): DateRange<D> {
        if (currentRange.start amp;amp; !currentRange.end) {
            const maxDate = this._dateAdapter.addCalendarDays(currentRange.start, this.delta);
            const rangeEnd = activeDate ? activeDate > maxDate ? maxDate : activeDate : null;
            return new DateRange(currentRange.start, rangeEnd);
        }

        return new DateRange<D>(null, null);
    }
}
  

Вы можете использовать in непосредственно в своем компоненте, например

 @Component({
  selector: "date-range-picker-selection-strategy-example",
  templateUrl: "date-range-picker-selection-strategy-example.html",
  providers: [
    {provide: 'rangeMax', useValue: 5},
    {
      provide: MAT_DATE_RANGE_SELECTION_STRATEGY,
      useClass: MaxRangeSelectionStrategy
    }
  ]
})
export class DateRangePickerSelectionStrategyExample {}
  

Но лучше создать директиву (не забудьте объявить в своем модуле)

Для этого сначала замените конструктор MaxRangeSelectionStrategy, чтобы удалить @Inject('rangeMax') и объявить переменную delta:

   public delta: number; //<--get out of constructor and make public
  constructor(private _dateAdapter: DateAdapter<D>) {}
  

Теперь создайте директиву:

 @Directive({
  selector: "[maxRange]",
  providers: [
    {
      provide: MAT_DATE_RANGE_SELECTION_STRATEGY,
      useClass: MaxRangeSelectionStrategy
    }
  ]
})
export class MaxRangeDirective {
  constructor(
    @Inject(MAT_DATE_RANGE_SELECTION_STRATEGY)
    private maxRangeStrategy: MaxRangeSelectionStrategy<any>
  ) {}
  @Input() set maxRange(value: number) {
    this.maxRangeStrategy.delta =  value || 7;
  }
}
  

И использовать в .html

 <mat-date-range-picker maxRange=5 #picker></mat-date-range-picker>
  

Наконец, мы можем добавить в styles.scss .css, например

 .mat-calendar-body-preview-end> .mat-calendar-body-cell-content {
  background-color: rgba(0, 0, 0, 0.04)!important;
}
  

Чтобы отметить как выбранный последний элемент

См. stackbliz

Обновите, если у нас есть мат-календарь. подход отличается. Только нам нужно использовать свойство dateClass.

 <mat-calendar #cal name="fecha" 
              [selected]="fecha" 
              (selectedChange)="fecha=$event;redraw(cal)"
              [dateClass]="dateClass">
</mat-calendar>
  

Где, например

 dateClass=(date: any) => {
  const days =
    (date.getTime() - this.fecha.getTime()) / (24 * 60 * 60 * 1000);
  return days >= 0 amp;amp; days < 5 ? 'red' : null;
};

redraw(cal:MatCalendar<any>)
{
    cal.updateTodaysDate() 
}
  

И у нас есть в styles.css

 .mat-calendar-body-cell.red {
    background: red;
}
  

Комментарии:

1. похоже, это не работает, когда применяется только к <mat-calendar>

2. @h11, если у вас есть только mat-календарь, подход другой (используйте только свойство dateClass , я обновляю вопрос, надеюсь, смогу вам помочь

Ответ №2:

Это лучше, чем предыдущий ответ, одним из основных недостатков предыдущего ответа является то, что вы сможете выбрать дату окончания меньше даты начала, которая на самом деле должна быть недействительной.

Ниже приведен лучший и простой способ реализации. Установив максимальную дату для диапазона дат

html-файл

  <mat-date-range-input [rangePicker]="picker" [max]="maxDate" >
      <input matStartDate placeholder="From Date" required formControlName="fromDate"  (dateChange)="setMaxDate()" >
      <input matEndDate placeholder="To Date" required formControlName="toDate" >
 </mat-date-range-input>

 <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
 <mat-date-range-picker (closed)="resetMaxDate()" #picker disabled="false"></mat-date-range-picker>
  

файл component.ts

 resetMaxDate()                                                          
{
  if(this.dateRangeForm.get('toDate').value )                             
  {                                                            
    this.maxDate = new Date(2122,1);
  }                                                                      
}


setMaxDate().                                                           
{
  let tempdate = new Date(this.dateRangeForm.get('fromDate').value);
  tempdate.setDate(tempdate.getDate()   30);
  this.maxDate = new Date(tempdate);                                    
}
  

также можно добавить средства проверки для ручного ввода

 constructor(private formBuilder: FormBuilder)                           
{
  this.dateRangeForm = this.formBuilder.group({
      fromDate: new FormControl(new Date(new Date().getTime() - 1000*3600*24*6), Validators.nullValidator),
      toDate: new FormControl(new Date(), [Validators.nullValidator,this.dateRangeValidator])
});         }




private dateRangeValidator: ValidatorFn = (): { [key: string]: any; } | null => {
  let invalid = false;
  const from = this.dateRangeForm amp;amp; 
  this.dateRangeForm.get("fromDate").value;
  const to = this.dateRangeForm amp;amp; this.dateRangeForm.get("toDate").value;
  if (from amp;amp; to) {
    invalid = new Date(from).valueOf()   1000*3600*24*30 < new Date(to).valueOf()  //checking if date difference is less than 31 days
}
  return invalid ? { invalidRange: { from, to } } : null;     };