Подтвердить что ты не робот

Компонент выбора даты и времени с угловым материалом?

Я импортировал средство выбора даты в проект и задавался вопросом, есть ли какой-нибудь официальный недавний компонент из angular материалов и материалов, чтобы также включить время в календарь.
Я видел много сборщиков времени в документации по материалам и исследовал множество сторонних, но они кажутся очень сложными.

4b9b3361

Ответ 1

К сожалению, ответ на ваш вопрос о том, есть ли официальная материальная поддержка для выбора времени, - "Нет", но в настоящее время это открытая проблема на официальном репозитории GitHub на Material2: https://github.com/angular/material2/issues/5648

Надеюсь, это скоро изменится, а вам придется бороться со сторонними, которых вы уже обнаружили. В этом выпуске GitHub есть несколько человек, которые предлагают свои собственные решения, которые вы можете попробовать.

Ответ 2

При использовании matInput с типом datetime-local можно использовать matInput datetime-local например:

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

Вы можете нажать на каждую часть заполнителя, чтобы установить день, месяц, год, часы, минуты и будь то AM или PM.

Ответ 3

Поскольку нет официальной системы выбора даты и времени от самого Angular, я бы посоветовал сделать комбинацию из стандартной программы выбора угловой даты и этой модели Angular Material Timepicker. Я выбрал этот, потому что все остальные, которые я обнаружил в это время, не поддерживают проблемы, устарели или не работают должным образом в самых последних версиях. Этот парень кажется очень отзывчивым.

Я обернул их обоих в один компонент, так что похоже, что это один блок. Вы просто должны сделать несколько вещей:

Когда еще не было предоставлено никакой информации, я бы посоветовал:

  • При щелчке по компоненту указатель даты всегда должен запускаться первым.
  • После того, как средство выбора даты закроется, оно автоматически откроется.
  • Используйте 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 (или, по крайней мере, сохраняться с добавлением правильного часового пояса).

Ответ 5

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

  1. Получить ссылку на оверлей DataPickers. Он выставляет его через _popupRef (это не свойство private). Тогда получите его хост-элемент.
  2. Когда выдает DateStickers OpenStream, создайте встроенное представление с вашим собственным компонентом выбора времени, возьмите его rootNodes и добавьте каждый к первому дочернему элементу 'элементов хоста' DatePickers.

Практически вся логика:

  ngAfterViewInit() {
    let viewRef: EmbeddedViewRef<any>;
    let portalHost: Node;

    this.picker.openedStream.subscribe(() => {
      viewRef = this.viewContainerRef.createEmbeddedView(this.pageActionsTmplRef);

      portalHost = this.picker._popupRef.hostElement;

      viewRef.rootNodes.forEach(n => {
        portalHost.firstChild.appendChild(n)
      })  
    })

    this.picker.closedStream.subscribe(() => {
      viewRef.rootNodes.forEach(n => {
        portalHost.firstChild.removeChild(n)
      })  
    })
  }

Некоторые из предстоящих проблем:

  1. Когда дата выбрана, по умолчанию она закрывает оверлей. Чтобы решить эту проблему, вы можете:
this.defaultCloseFunc = this.picker.close;
this.picker.close = () => {};

// And when need to close:
this.picker.close = this.defaultCloseFunc;
this.picker.close()

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

  1. Вероятно, понадобятся некоторые изменения CSS. Ex. display: block, box-shadow: none и т.д. Также анимация происходит при отображении сборщика. Для этого вы можете удалить его, импортировав модуль NoopAnimation из Material.

Смотрите ДЕМО здесь.

Ответ 6

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

  1. Получить ссылку на оверлей DataPickers. Он выставляет его через _popupRef (это не свойство private). Тогда получите его хост-элемент.
  2. Когда выдает DateStickers OpenStream, создайте встроенное представление с вашим собственным компонентом выбора времени, возьмите его rootNodes и добавьте каждый к первому дочернему элементу 'элементов хоста' DatePickers.
      ngAfterViewInit() {
        let viewRef: EmbeddedViewRef<any>;
        let overlayHost: Node;

        this.picker.openedStream.subscribe(() => {
          viewRef = this.viewContainerRef.createEmbeddedView(this.timePickerRef);

          overlayHost = this.picker._popupRef.hostElement;

          viewRef.rootNodes.forEach(n => {
            overlayHost.firstChild.firstChild.appendChild(n)
          })  
        })

        this.picker.closedStream.subscribe(() => {
          viewRef.rootNodes.forEach(n => {
            overlayHost.firstChild.firstChild.removeChild(n)
          })  
        })
      }

А когда выбирается дата, по умолчанию закрывается оверлей. Чтобы решить эту проблему, вы можете:

this.defaultCloseFunc = this.picker.close;
this.picker.close = () => {};

// And when need to close:
this.picker.close = this.defaultCloseFunc;
this.picker.close()

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

Смотрите ДЕМО здесь.