作为功能请求有一个未解决的问题:
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