【问题标题】:How to implement ranged inline calendar with angular material?如何使用角度材料实现范围内联日历?
【发布时间】:2021-08-01 16:10:41
【问题描述】:

我想使用材料日历作为范围内联日历来显示和插入日期范围。 使用 mat-date-range-picker 时,这只是工作(但不是内联)。 使用 mat-calendar 时,它适用于内联,但不适用于远程。 但是如果我将 selectedRangeValue 作为 DateRange 而不是 Date 传递,则 Range 会正确显示。

唯一仍然缺少的是输入......

这是我现在使用的代码(略):

<mat-calendar (selectedChange)="selectedRangeChange($event)"
              [selected]="selectedRangeValue"
>
</mat-calendar>
selectedRangeValue: DateRange<Date> =  new DateRange<Date>(this.selectedValue.begin, this.selectedValue.end);

我必须这样做,因为土星日期选择器仅在 Angular 9 之前受支持,而从 Angular 10 开始,Material Datepicker 支持日期范围。 但是这个“内联日期范围日历”我就是无法工作......

我发现https://github.com/angular/components/issues/20697 描述了同样的问题并找到了解决方案,但没有写下来,所以没有帮助。

我也试过去了解angular material datepicker的源码,可惜还是没搞懂。如有任何帮助或提示,我将不胜感激。

【问题讨论】:

    标签: angular angular-material


    【解决方案1】:

    我找到了解决这个问题的方法。虽然我不确定这是否是最好的方法,但我想提供解决方案。

    我必须为内联范围日历创建一个新的角度组件:

    HTML 架构

    <mat-calendar
      #calendar
      [selected]="selectedRangeValue"
      (selectedChange)="selectedChange($event)"
    >
    </mat-calendar>
    

    打字稿

    import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
    import {
      DateRange, DefaultMatCalendarRangeStrategy,
      MAT_DATE_RANGE_SELECTION_STRATEGY, MatCalendar,
    } from '@angular/material/datepicker';
    import {isNullOrUndefined} from '../../util/is-null-or-undefined';
    import moment from 'moment';
    
    @Component({
      // tslint:disable-next-line:component-selector
      selector: 'inline-range-calendar',
      templateUrl: './inline-range-calendar.component.html',
      styleUrls: ['./inline-range-calendar.component.sass'],
      providers: [{
        provide: MAT_DATE_RANGE_SELECTION_STRATEGY,
        useClass: DefaultMatCalendarRangeStrategy
      }]
    })
    export class InlineRangeCalendarComponent implements OnInit {
    
      @ViewChild(MatCalendar) calendar: MatCalendar<Date>;
    
      @Input() selectedRangeValue: DateRange<Date>;
      @Output() selectedRangeValueChange = new EventEmitter<DateRange<Date>>();
    
      ngOnInit(): void {
     }
    
    selectedChange($event) {
    const m = moment($event);
    
    if (!isNullOrUndefined(this.selectedRangeValue.end)) {
      const start = this.selectedRangeValue.start;
      // @ts-ignore the parser thinks that this is a date, but it is a moment.js object, so this will work
      start.set(m.toObject());
      this.selectedRangeValue = new DateRange<Date>(start, undefined);
      this.selectedRangeValueChange.emit(this.selectedRangeValue);
    } else {
      const end = (!isNullOrUndefined(this.selectedRangeValue.end)) ? this.selectedRangeValue.end : moment(m);
      // @ts-ignore the parser thinks that this is a date, but it is a moment.js object, so this will work
      end.set(m.toObject());
      // @ts-ignore the parser thinks that this is a date, but it is a moment.js object, so this will work
      this.selectedRangeValue = new DateRange<Date>(this.selectedRangeValue.start, end);
      if (this.selectedRangeValue.end < this.selectedRangeValue.start)  {
        this.selectedRangeValue = new DateRange<Date>(this.selectedRangeValue.end, this.selectedRangeValue.start);
      }
      this.selectedRangeValueChange.emit(this.selectedRangeValue);
    }
    }
    
    }
    

    希望这可以帮助某人。

    【讨论】:

    • 哦,我的 - 虽然这使用 chrome 86 就像一个魅力,但在 Firefox(旧 52 和现代 88)中,在选择开始或结束日期时,范围内的内联日历需要双击而不是单击.
    • 好的,用 selectedChange 事件替换 selectstart 事件的使用修复了 Firefox 问题,同时使代码更容易(不再进行字符串解析,只需直接使用 moment、js 日期)
    • 感谢分享帮助。我在stackblitz 做了一个小项目来重新创建这个组件
    • @VinceH 感谢您创建堆栈闪电战。我从来没有时间学习如何做到这一点。这将有助于其他人了解如何解决这个问题。
    【解决方案2】:

    建议的解决方案有很多额外的代码,更重要的是它不适合我。这是一个精简的工作版本。

    HTML

    <mat-calendar [selected]="selectedRangeValue"
      (selectedChange)="selectedChange($event)">
    </mat-calendar>
    

    TS

    import { Component, EventEmitter, Input, Output } from '@angular/core';
    import { DateRange } from '@angular/material/datepicker';
    
    @Component({
        selector: 'inline-range-calendar',
        templateUrl: './inline-range-calendar.component.html',
    })
    export class InlineRangeCalendarComponent {
    
        @Input() selectedRangeValue: DateRange<Date> | undefined;
        @Output() selectedRangeValueChange = new EventEmitter<DateRange<Date>>();
    
        selectedChange(m: any) {
            if (!this.selectedRangeValue?.start || this.selectedRangeValue?.end) {
                this.selectedRangeValue = new DateRange<Date>(m, null);
            } else {
                const start = this.selectedRangeValue.start;
                const end = m;
                if (end < start) {
                    this.selectedRangeValue = new DateRange<Date>(end, start);
                } else {
                    this.selectedRangeValue = new DateRange<Date>(start, end);
                }
            }
            this.selectedRangeValueChange.emit(this.selectedRangeValue);
        }
    
    }
    

    【讨论】:

    • 看起来简洁多了,谢谢分享!我现在在做其他事情,但我会要求我的同事检查这一点并相应地更改我们的代码
    • @JulianEgner 谢谢你的第一个版本,我从那里开始:)
    • 太棒了!我希望该代码可能对某人有所帮助,因为花了几天时间才找到解决方案。
    • 能否为您的解决方案提供 stackblitz?
    猜你喜欢
    • 1970-01-01
    • 2018-04-20
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多