Angular-Material日期时间选择器组件?

92

我在项目中引入了一个日期选择器,想知道是否有来自angular和material的官方最近组件,也可以在日历中包括时间。
我看过许多材料文档中的时间选择器,并研究了很多第三方选择器,但它们似乎非常复杂。


有一个很好的包https://www.npmjs.com/package/@dhutaryan/ngx-mat-timepicker,但只有时间选择器。 - undefined
9个回答

92

当使用matInput的类型为datetime-local时,您可以拥有一个日期时间选择器,例如:

  <mat-form-field>
    <input matInput type="datetime-local" placeholder="start date">
  </mat-form-field>

您可以点击占位符的每个部分来设置日期、月份、年份、小时、分钟以及是上午还是下午。


4
有没有一种方法可以设置输入的区域设置?比如以24小时制的dd/mm/yyyy --:--格式显示时间? - mohax
28
截至2019年10月,这在 Chrome 和 MS Edge 上的效果非常好。目前为止,Firefox 不支持 type="datetime-local",它只把它当作普通输入框处理。caniuse 统计数据 - mhodges
3
截至本周(2021年10月5日),Firefox 支持 type="datetime-local"。 - icguy
1
火狐浏览器版本96(截至2022年1月28日最新版本),它有点能用,但上午/下午元素的一半被X按钮遮挡,弹出小部件也不显示时间元素,只显示日历...所以,是的,不行。 - fartwhif
1
你可以使用<input type="time" id="appt" name="appt" min="09:00" max="18:00" required>来仅输入时间。 - Mark Homer
显示剩余2条评论

43

15
这个库对于语言环境的支持缺乏详细的文档,并且样例代码存在显著的错误。除非你碰巧需要使用默认设置(美国本土语言环境),否则几乎不可能使用。已经有请求数月之久,但是没有看到明显的活动来解决这些问题。我建议避免使用。 - HughHughTeotl
对于Angular 8(以及其他旧版本,我猜测),您需要fork整个项目并进行修正才能使其正常工作... https://github.com/h2qutc/angular-material-components/issues/53 - fartwhif

32
长期以来,由于angular本身没有官方的日期时间选择器,我建议将默认的angular日期选择器Angular Material Timepicker结合使用。我选择了这个时间选择器,因为我在此时发现的所有其他选择器都缺乏支持问题、已过时或在最新的angular版本中无法正常工作。这个人似乎非常负责。
我将它们两个都包装在一个组件中,使它看起来像是一个单元。你只需要确保做几件事情:
当没有输入时,我建议:
  • 在组件上点击时,应先触发日期选择器。
  • 日期选择器关闭后,自动弹出时间选择器。
  • 在日期选择器上使用touchUi = true,这样日期选择器和时间选择器就会依次显示为对话框。
  • 确保日期选择器也在表单上显示(而不仅仅是默认的图标)。
  • 使用这个解决方案在材料表单中也使用时间选择器,当它们放在一起时,看起来就像是一个表单。
给定一个值后,很明显一部分包含时间,另一部分包含日期。此时明确用户必须点击时间更改时间,点击日期更改日期。但在此之前,也就是两个字段都为空(并且作为一个字段“附加”在一起)时,您应该确保用户不会混淆,按照上述建议进行操作。我的组件还没有完成,我会尽力记住稍后分享代码。如果这个问题已经超过一个月左右,请发表评论。编辑:结果

enter image description here

<div fxLayout="row">
  <div *ngIf="!dateOnly" [formGroup]="timeFormGroup">
    <mat-form-field>
      <input matInput [ngxTimepicker]="endTime" [format]="24"  placeholder="{{placeholderTime}}" formControlName="endTime" />
    </mat-form-field>
    <ngx-material-timepicker #endTime (timeSet)="timeChange($event)" [minutesGap]="10"></ngx-material-timepicker>
  </div>
  <div>
    <mat-form-field>
      <input id="pickerId" matInput [matDatepicker]="datepicker" placeholder="{{placeholderDate}}" [formControl]="dateForm"
             [min]="config.minDate" [max]="config.maxDate" (dateChange)="dateChange($event)">
      <mat-datepicker-toggle matSuffix [for]="datepicker"></mat-datepicker-toggle>
      <mat-datepicker #datepicker [disabled]="disabled" [touchUi]="config.touchUi" startView="{{config.startView}}"></mat-datepicker>
    </mat-form-field>
  </div>
</div>

import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { DateAdapter, MatDatepickerInputEvent } from '@angular/material';

import * as moment_ from 'moment';

const moment = moment_;

import { MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';

class DateConfig {
  startView: 'month' | 'year' | 'multi-year';
  touchUi: boolean;
  minDate: moment_.Moment;
  maxDate: moment_.Moment;
}

@Component({
  selector: 'cb-datetimepicker',
  templateUrl: './cb-datetimepicker.component.html',
  styleUrls: ['./cb-datetimepicker.component.scss'],
})
export class DatetimepickerComponent implements OnInit {

  @Input() disabled: boolean;
  @Input() placeholderDate: string;
  @Input() placeholderTime: string;
  @Input() model: Date;
  @Input() purpose: string;
  @Input() dateOnly: boolean;

  @Output() dateUpdate = new EventEmitter<Date>();

  public pickerId: string = "_" + Math.random().toString(36).substr(2, 9);

  public dateForm: FormControl;
  public timeFormGroup: FormGroup;
  public endTime: FormControl;

  public momentDate: moment_.Moment;
  public config: DateConfig;

  //myGroup: FormGroup;


  constructor(private adapter : DateAdapter<any>) { }

  ngOnInit() {

    this.adapter.setLocale("nl-NL");//todo: configurable
    this.config = new DateConfig();
    if (this.purpose === "birthday") {
      this.config.startView = 'multi-year';
      this.config.maxDate = moment().add('year', -15);
      this.config.minDate = moment().add('year', -90);
      this.dateOnly = true;
    } //add more configurations
    else {
      this.config.startView = 'month';
      this.config.maxDate = moment().add('year', 100);
      this.config.minDate = moment().add('year', -100);
    }


    if (window.screen.width < 767) {
      this.config.touchUi = true;
    }



    if (this.model) {
      var mom = moment(this.model);
      if (mom.isBefore(moment('1900-01-01'))) {
        this.momentDate = moment();
      } else {
        this.momentDate = mom;
      }
    } else {
      this.momentDate = moment();
    }

    this.dateForm = new FormControl(this.momentDate);
    if (this.disabled) {
      this.dateForm.disable();
    }
    this.endTime = new FormControl(this.momentDate.format("HH:mm"));

    this.timeFormGroup = new FormGroup({
      endTime: this.endTime
    });


  }


  public dateChange(date: MatDatepickerInputEvent<any>) {

    if (moment.isMoment(date.value)) {
      this.momentDate = moment(date.value);
      if (this.dateOnly) {
        this.momentDate = this.momentDate.utc(true);
      } 
      var newDate = this.momentDate.toDate();
      this.model = newDate;
      this.dateUpdate.emit(newDate);
    }

    console.log("datechange",date);
  }

  public timeChange(time: string) {

    var splitted = time.split(':');
    var hour = splitted[0];
    var minute = splitted[1];

    console.log("time change", time);
   this.momentDate = this.momentDate.set('hour', parseInt(hour));
    this.momentDate = this.momentDate.set('minute', parseInt(minute));

    var newDate = this.momentDate.toDate();
    this.model = newDate;
    this.dateUpdate.emit(newDate);
  }
}

一个重要的来源: https://github.com/Agranom/ngx-material-timepicker/issues/126

我认为它仍然需要一些调整,因为我认为如果我有更多时间创建它的话,它可以工作得更好。最重要的是,我尝试解决UTC问题,所以所有日期应该显示为本地时间,但应该以UTC格式发送到服务器(或者至少保存时加上正确的时区)。


1
大约一个月前...好奇你的解决方案 :-). - Tim Harker
1
@TimHarker 我对结果不是完全满意,需要一些调整,但我认为这是一个很好的开端。我没有解决我提到的需求,它仍然在待办清单上。因此,请随意完成我的要求并更新答案 :) - CularBytes
Agranom库已经过时(最高支持Angular 8)。 - Pedro Bezanilla
谢谢你提醒我,Pedro。看起来他现在比两年前少了一些活跃度,对于开源库来说,让某人保持对项目的承诺总是很困难的。 - CularBytes

29

很遗憾,关于您的问题是否有官方材料支持选择时间的答案是“否”,但这是目前在官方Material2 GitHub存储库中的一个公开问题:https://github.com/angular/material2/issues/5648

希望这很快会改变,同时,您将不得不使用已经发现的第三方支持。在那个GitHub问题中,有一些人提供了他们自己制作的解决方法,您可以尝试一下。


@KevinCurnow:你能分享一下解决方案吗?我需要一个日期范围选择器和时间选择器。另一个限制是我需要让它在Angular4中工作。我知道有一个可用于6的版本。 - TheMonkWhoSoldHisCode

4

目前有一个官方的Open Issue要求Angular团队支持时间和日期选择器,这个Issue已经存在4年了:

https://github.com/angular/components/issues/5648

有很多库可用,你可以在这篇帖子中看到,但问题是它们中大多数已被弃用(只适用于旧版本的Angular)。

因此,我实际上正在使用@matheo制作的这款日期选择器,它是我找到的最新和最维护的:

https://www.npmjs.com/package/@coachcare,它最近被移动到https://www.npmjs.com/package/@matheo/datepicker


Demo: http://matheo.co/demos/datepicker

NOTE: If it doesn't work for Angular version >= 12 please check that new Angular convention theming is being used, e.g.:

@use "~@matheo/datepicker/theming" as datepicker;
// ...
@include datepicker.mat-datepicker-theme($main_theme);

这个支持为日期选择器设置时区吗?@Pedro - alext
嗨@AleksandarT,理论上是可以的,但我在实现过程中遇到了很多麻烦...... - Pedro Bezanilla
@matheo/datepicker 包包含 .mjs 文件,Node.js 将其视为 ECMAScript 模块,但这种方式不受我的应用程序配置支持。我尝试过使用 webpack 模块配置进行调整,但现在已经放弃了。导入该组件会导致数百个错误,例如: Microsoft.AspNetCore.SpaServices: Error: ERROR in ../node_modules/@matheo/datepicker/core/datetime/index.d.ts:8:21 - error TS2694: Namespace '"C:/projects/app/node_modules/@angular/core/core"' has no exported member '╔╡╔╡FactoryDeclaration'. - fartwhif

4

实现日期和时间控件的最佳方式是使用输入类型“datetime-local”。

请参考https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local

示例代码如下:

<mat-grid-list [cols]="5" gutterSize="20px" rowHeight="70px">
                <form *ngIf="dateForm && dateForm !== null" [formGroup]="dateForm">
                    <mat-grid-tile>
                        <mat-form-field appearance="outline" floatLabel="auto">
                            <mat-label>From Date</mat-label>
                            <input type="datetime-local" matInput name="fromTime" formControlName="fromDate">
                        </mat-form-field>
                    </mat-grid-tile>
                    <mat-grid-tile>
                        <mat-form-field appearance="outline" floatLabel="auto">
                            <mat-label>To Date</mat-label>
                            <input type="datetime-local" matInput name="toTime" formControlName="toDate">
                        </mat-form-field>
                    </mat-grid-tile>
                </form>
</mat-grid-list>

这个程序可以工作,但是在Firefox和Chrome中会有不同的功能。在Chrome中,你会看到一个小模拟时钟图标,点击它会弹出一个下拉式辅助菜单,但是在Firefox中却没有这些功能。 - fartwhif

2

1

有没有想法如何解决这个 day-time-picker 的问题?ERROR TypeError: Cannot read property 'locale' of undefined at DayTimeCalendarService.push../node_modules/ng2-date-picker/fesm5/ng2-date-picker.js.DayTimeCalendarService.getConfig (ng2-date-picker.js:999) at DayTimeCalendarComponent.push../node_modules/ng2-date-picker/fesm5/ng2-date-picker.js.DayTimeCalendarComponent.init (ng2-date-picker.js:1691) at DayTimeCalendarComponent.push../node_modules/ng2-date-picker/fesm5/ng2-date-picker.js.DayTimeCalendarComponent.ngOnInit (ng2-date-picker.js:1687) - Kirataka
我也安装了依赖项(moment & tslib)。 - Kirataka
你需要moment本地化吧。 - vlio20
我已经将 moment-with-locales.min.js 文件添加到我的资产文件夹中。 - Kirataka
我建议您在 Github 存储库中打开一个问题,StackBlitz 将会提供帮助。 - vlio20

-2
另一种使用方法:mat-datepicker-actions
{{label}}
    <mat-datepicker #picker>
      <mat-datepicker-actions>
        <input [ngxTimepicker]="pickerTime">
        <ngx-material-timepicker #pickerTime></ngx-material-timepicker>
      </mat-datepicker-actions>
    </mat-datepicker>
  </mat-form-field>

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接