这篇文章旨在解释如何从头开始在Angular中创建一个日期范围选择器组件。这是我的第一篇文章的续作,可以在下面的链接找到:https://medium.com/stackademic/creating-a-custom-date-time-picker-in-angular-5f00591d641c。另外,在我的下一篇专栏中,我将写到自定义组件的好处。
范围选取器
创造背后的用意在我制作这个组件之前,我在寻找一个易于调整样式的通用日期选择工具,它需要提供日期选择、时间选择和范围选择。我需要一个简单的工具,在寻找的过程中发现,大多数日期选择器要么太复杂,要么不具备我所需要的这些功能。
我需要的功能:- 功能一
-
功能二
(注:此处可以根据具体功能继续添加) - 日期和时间选择器
- 仅日期选择器
- 仅时间选择器
- 日期范围选择器
在我们还没有这一切之前,唯一缺失的就是范围功能,所以我先用简单的语言描述了逻辑。
请注意,所有代码都可以在 Github 上找到:https://github.com/marekpanti/dateTimePicker,我还创建了一个 npm 包:@marekpanti/angular-date-time-picker
逻辑部分我需要两个不同的输出,一个是当 range=false
时,表示单一选择。另一个是日期范围数组的情况,selectedRange[0]
是起始选择,selectedRange[1]
是结束选择。
然后我需要检查范围,以便能够可视化地显示选择。
inRangeSelection(i: number) {
const currentDay = new Date(
this.date.getFullYear(),
this.date.getMonth(),
i
);
return (
this.range &&
this.clickedDate &&
this.clickedToDate &&
this.clickedDate.getDate() < currentDay.getDate() &&
this.clickedToDate.getDate() > currentDay.getDate() &&
this.clickedDate.getFullYear() >= currentDay.getFullYear() &&
this.clickedToDate.getFullYear() >= currentDay.getFullYear() &&
this.clickedDate.getMonth() >= currentDay.getMonth() &&
this.clickedToDate.getMonth() >= currentDay.getMonth()
);
}
然后实际的设置方法稍微复杂一点,我在代码中特意添加了注释进行说明。这里条件的顺序尤其重要。
setDate(index: number) {
// 如果已经存在范围选择,顺序很重要
if (this.range) {
// 如果已经存在范围选择,并且用户点击新的选择,我们就重新开始
if (this.clickedDate && this.clickedToDate) {
this.clickedDate = null;
this.clickedToDate = null;
}
// 如果已经有第一次选择了,那么将值赋给 clickedToDate(即当前日期)
if (this.clickedDate && !this.clickedToDate) {
this.clickedToDate = new Date(
this.date.getFullYear(),
this.date.getMonth(),
index
);
}
// 只在这里我们设置第一个重要的起点
if (!this.clickedToDate && !this.clickedDate) {
this.clickedDate = new Date(
this.date.getFullYear(),
this.date.getMonth(),
index
);
}
// 如果 clickedToDate 早于 clickedDate,那么我们会交换它们的位置
if (
this.clickedDate &&
this.clickedToDate &&
this.clickedDate > this.clickedToDate
) {
const clickedToDate = this.clickedDate;
const clickedDate = this.clickedToDate;
this.clickedDate = clickedDate;
this.clickedToDate = clickedToDate;
}
// 否则,进入正常模式
} else {
this.clickedDate = new Date(
this.date.getFullYear(),
this.date.getMonth(),
index
);
}
}
最后,输出方式变了。
confirm() {
if (this.range) {
// 如果点击了开始日期和结束日期,则触发选择范围事件
if (this.clickedDate && this.clickedToDate) {
this.selectRange.emit([this.clickedDate, this.clickedToDate]);
}
} else {
// 如果仅点击了日期,则设置小时和分钟,并触发选择日期事件
if (this.clickedDate) {
this.clickedDate.setHours(this.timeForm.get('hours')?.value || 0);
this.clickedDate.setMinutes(this.timeForm.get('minutes')?.value || 0);
this.selectDate.emit(this.clickedDate);
}
}
}
为什么所有的逻辑都在这个组件里?
如果你查看了仓库中的整个组件:https://github.com/marekpanti/dateTimePicker/blob/master/projects/marekpanti/angular-date-time-picker/src/lib/angular-date-time-picker.component.ts,你可能会疑惑:为什么马雷克要把所有的逻辑都放在组件里,而不是像他说的那样使用门面和干净的逻辑呢?
规则没有例外怎么行呢?在处理复杂组件和UI时,我只是想把我的逻辑放在一个地方,因为我理解我的组件是视觉上的,每个属性都与每个方法相关联,如果方法分散在不同的服务中,对我来说这样更难读。
最后总结一下,这篇文章总结了如何创建一个自定义组件,乍一看可能很复杂,但实际上只要计划周全并且理解透彻,你就能轻松地创建你自己的组件。
栈学 🎓谢谢您看到最后。在您离开前:
- 请给作者点个赞并关注他/她!👏
- 关注我们 X | LinkedIn | YouTube | Discord
- 浏览我们的其他平台:In Plain English | CoFeed | Differ
- 更多内容请浏览 Stackademic.com