1

我找不到 Angular 7 的任何 DateTime 选择器。所以我决定结合 Date Picker 和 Time Picker

https://ng-bootstrap.github.io/#/components/datepicker

https://ng-bootstrap.github.io/#/components/timepicker

<ng-template #dateTimePicker>
  <ngb-datepicker #createdStartDate name="datepicker"></ngb-datepicker>
  <ngb-timepicker #createdStartTime name="timepicker" [meridian]="true"></ngb-timepicker>
</ng-template>

<form [formGroup]="managePromotionsForm" 
    <div class="col-md-6">
      <div class="row form-group">
        <label class="col-md-4 control-label" for="createdStartDate" translate="">Created From </label>
        <div class="col-md-6">
          <div class="input-group">
            <input readOnly class="form-control" id="createdStartDate" placeholder="From Date"
              [formControl]="controls['createdStartDate']">
              
              
            <div class="input-group-append">
              <button class="btn btn-outline-secondary calendar" [ngbPopover]="dateTimePicker"  type="button"></button>
            </div>
          </div>
        </div>
      </div>
    </div>
</form>

这是我到目前为止所拥有的

日期时间选择器

现在如何在createdStartDate文本框中显示选定的日期和时间?

4

3 回答 3

3

如果您愿意,可以使用 ngbDropDown、ngbDatePicker 和 ngbTimePicker 的组合

为此,您需要两个变量和一个吸气剂

  date: any;
  time:any= {hour:0,minute:0};

  _value;
  label;
  ngOnInit()
  {
    this.getDatetime()
  }
  getDatetime() {
    let value = null;
    if (!this.date) {
      if (!this.time) value = "yyyy/MM/dd hh:mm";
      else
        value =
          "yyyy/MM/dd " +
          ("0" + this.time.hour).slice(-2) +
          ":" +
          ("0" + this.time.minute).slice(-2);
    }
    if (!value) {
      value = new Date(Date.UTC(
        this.date.year,
        this.date.month - 1,
        this.date.day,
        this.time ? this.time.hour : 0,
        this.time ? this.time.minute : 0
      );
      this._value=value;
   } else 
      this._value=null

   this.form.get("control").setValue(this._value);
   this.label=value;
  }

<form [formGroup]="form">
  <div ngbDropdown>
  <button class="datepicker btn btn-link"  ngbDropdownToggle>{{_value?(_value|date:'medium'):label}}</button>
      <div ngbDropdownMenu >
        <ngb-datepicker #dp [(ngModel)]="date" (dateSelect)="getDatetime()"[ngModelOptions]="{standalone:true}" ></ngb-datepicker>
        <ngb-timepicker [ngModel]="time" (ngModelChange)="time=$event;getDatetime()"[ngModelOptions]="{standalone:true}"></ngb-timepicker>
      </div>
      </div>
  <button class="btn btn-primary">submit</button>

参见stackblitz

注意:在这种情况下,我们将创建一个自定义表单控件以不产生依赖

出于好奇而更新,在stackblitz中我制作了自定义表单控件

于 2020-08-30T10:21:00.453 回答
1

如果要创建可以绑定的表单元素。更好的方法是创建一个单独的组件并实现控制值访问器

然后您可以将该组件用作表单元素并将 ngModal 或表单控件绑定到它。

于 2020-08-30T08:15:42.220 回答
1

作为功​​能请求有一个未解决的问题: https ://github.com/ng-bootstrap/ng-bootstrap/issues/2086

datepicker 和 timepicker 可以与一些自定义代码结合使用。 在此处输入图像描述

这是一个示例,它使用带有按钮的文本输入来打开日期选择器并在时间选择器之间切换。

HTML:

<div class="input-group mr-2">
    <input
    [ngClass]="ngControl?.valid ? 'ng-valid' : 'ng-invalid'"
    class="form-control"
    (blur)="inputBlur($event)"
    [ngModel]="dateString | date:inputDatetimeFormat"
    (change)="onInputChange($event)"
    [disabled]="disabled"
  />

    <div class="input-group-append">
        <button
      class="btn btn-outline-secondary"
      [ngbPopover]="calendarContent"
      [disabled]="disabled"
      type="button"
    >
      <fa-icon [icon]="['far', 'calendar']"></fa-icon>
    </button>
    </div>
</div>

<ng-template #calendarContent>
    <div>
        <div *ngIf="!showTimePickerToggle">
            <ngb-datepicker id="dp" #dp name="datepicker" [ngModel]="datetime"
                (ngModelChange)="onDateChange($event, dp)"></ngb-datepicker>
            <button
        class="btn btn-block btn-outline-secondary"
        [disabled]="!datetime?.day"
        [ngbPopover]="timePickerContent"
        type="button"
        (click)="toggleDateTimeState($event)"
      >
        <fa-icon [icon]="['far', 'clock']"></fa-icon>
      </button>
        </div>
        <div *ngIf="showTimePickerToggle">
            <button
        class="btn btn-block btn-outline-secondary"
        [ngbPopover]="calendarContent"
        type="button"
        (click)="toggleDateTimeState($event)"
      >
        <fa-icon [icon]="['far', 'calendar']"></fa-icon>
      </button>
            <div class="mt-auto">
                <ngb-timepicker #tp name="timepicker" [ngModel]="datetime" (ngModelChange)="onTimeChange($event)"
                    [seconds]="seconds" [hourStep]="hourStep" [minuteStep]="minuteStep" [secondStep]="secondStep">
                </ngb-timepicker>
            </div>
        </div>
    </div>
</ng-template>

TS:

import {
  Component,
  OnInit,
  Input,
  forwardRef,
  ViewChild,
  AfterViewInit,
  Injector
} from "@angular/core";
import {
  NgbTimeStruct,
  NgbDateStruct,
  NgbPopoverConfig,
  NgbPopover,
  NgbDatepicker
} from "@ng-bootstrap/ng-bootstrap";
import {
  NG_VALUE_ACCESSOR,
  ControlValueAccessor,
  NgControl
} from "@angular/forms";
import { DatePipe } from "@angular/common";
import { DateTimeModel } from "./date-time.model";
import { noop } from "rxjs";

@Component({
  selector: "app-date-time-picker",
  templateUrl: "./date-time-picker.component.html",
  styleUrls: ["./date-time-picker.component.scss"],
  providers: [
    DatePipe,
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DateTimePickerComponent),
      multi: true
    }
  ]
})
export class DateTimePickerComponent
  implements ControlValueAccessor, OnInit, AfterViewInit {
  @Input()
  dateString: string;

  @Input()
  inputDatetimeFormat = "M/d/yyyy H:mm:ss";
  @Input()
  hourStep = 1;
  @Input()
  minuteStep = 15;
  @Input()
  secondStep = 30;
  @Input()
  seconds = true;

  @Input()
  disabled = false;

  private showTimePickerToggle = false;

  private datetime: DateTimeModel = new DateTimeModel();
  private firstTimeAssign = true;

  // @ViewChild(NgbDatepicker, { static: true })
  // private dp: NgbDatepicker;

  @ViewChild(NgbPopover, { static: true })
  private popover: NgbPopover;

  private onTouched: () => void = noop;
  private onChange: (_: any) => void = noop;

  private ngControl: NgControl;

  constructor(private config: NgbPopoverConfig, private inj: Injector) {
    config.autoClose = "outside";
    config.placement = "auto";
  }

  ngOnInit(): void {
    this.ngControl = this.inj.get(NgControl);
  }

  ngAfterViewInit(): void {
    this.popover.hidden.subscribe($event => {
      this.showTimePickerToggle = false;
    });
  }

  writeValue(newModel: string) {
    if (newModel) {
      this.datetime = Object.assign(
        this.datetime,
        DateTimeModel.fromLocalString(newModel)
      );
      this.dateString = newModel;
      this.setDateStringModel();
    } else {
      this.datetime = new DateTimeModel();
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  toggleDateTimeState($event) {
    this.showTimePickerToggle = !this.showTimePickerToggle;
    $event.stopPropagation();
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  onInputChange($event: any) {
    const value = $event.target.value;
    const dt = DateTimeModel.fromLocalString(value);

    if (dt) {
      this.datetime = dt;
      this.setDateStringModel();
    } else if (value.trim() === "") {
      this.datetime = new DateTimeModel();
      this.dateString = "";
      this.onChange(this.dateString);
    } else {
      this.onChange(value);
    }
  }

  onDateChange($event: string | NgbDateStruct, dp: NgbDatepicker) {
    const date = new DateTimeModel($event);

    if (!date) {
      this.dateString = this.dateString;
      return;
    }

    if (!this.datetime) {
      this.datetime = date;
    }

    this.datetime.year = date.year;
    this.datetime.month = date.month;
    this.datetime.day = date.day;

    const adjustedDate = new Date(this.datetime.toString());
    if (this.datetime.timeZoneOffset !== adjustedDate.getTimezoneOffset()) {
      this.datetime.timeZoneOffset = adjustedDate.getTimezoneOffset();
    }

    this.setDateStringModel();
  }

  onTimeChange(event: NgbTimeStruct) {
    this.datetime.hour = event.hour;
    this.datetime.minute = event.minute;
    this.datetime.second = event.second;

    this.setDateStringModel();
  }

  setDateStringModel() {
    this.dateString = this.datetime.toString();

    if (!this.firstTimeAssign) {
      this.onChange(this.dateString);
    } else {
      // Skip very first assignment to null done by Angular
      if (this.dateString !== null) {
        this.firstTimeAssign = false;
      }
    }
  }

  inputBlur($event) {
    this.onTouched();
  }
}
import { NgbTimeStruct, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
import { DatePipe } from "@angular/common";

export interface NgbDateTimeStruct extends NgbDateStruct, NgbTimeStruct {}

export class DateTimeModel implements NgbDateTimeStruct {
  year: number;
  month: number;
  day: number;
  hour: number;
  minute: number;
  second: number;

  timeZoneOffset: number;

  public constructor(init?: Partial<DateTimeModel>) {
    Object.assign(this, init);
  }

  public static fromLocalString(dateString: string): DateTimeModel {
    const date = new Date(dateString);

    const isValidDate = !isNaN(date.valueOf());

    if (!dateString || !isValidDate) {
      return null;
    }

    return new DateTimeModel({
      year: date.getFullYear(),
      month: date.getMonth() + 1,
      day: date.getDate(),
      hour: date.getHours(),
      minute: date.getMinutes(),
      second: date.getSeconds(),
      timeZoneOffset: date.getTimezoneOffset()
    });
  }

  private isInteger(value: any): value is number {
    return (
      typeof value === "number" &&
      isFinite(value) &&
      Math.floor(value) === value
    );
  }

  public toString(): string {
    if (
      this.isInteger(this.year) &&
      this.isInteger(this.month) &&
      this.isInteger(this.day)
    ) {
      const year = this.year.toString().padStart(2, "0");
      const month = this.month.toString().padStart(2, "0");
      const day = this.day.toString().padStart(2, "0");

      if (!this.hour) {
        this.hour = 0;
      }
      if (!this.minute) {
        this.minute = 0;
      }
      if (!this.second) {
        this.second = 0;
      }
      if (!this.timeZoneOffset) {
        this.timeZoneOffset = new Date().getTimezoneOffset();
      }

      const hour = this.hour.toString().padStart(2, "0");
      const minute = this.minute.toString().padStart(2, "0");
      const second = this.second.toString().padStart(2, "0");

      const tzo = -this.timeZoneOffset;
      const dif = tzo >= 0 ? "+" : "-",
        pad = function(num) {
          const norm = Math.floor(Math.abs(num));
          return (norm < 10 ? "0" : "") + norm;
        };

      const isoString = `${pad(year)}-${pad(month)}-${pad(day)}T${pad(
        hour
      )}:${pad(minute)}:${pad(second)}${dif}${pad(tzo / 60)}:${pad(tzo % 60)}`;
      return isoString;
    }

    return null;
  }
}

这是一个工作示例的链接 https://stackblitz.com/edit/angular-datetimepicker

于 2020-12-22T20:37:44.643 回答