手记

Angular中打造自定义日期范围选择器:一步步教你实现

这篇文章旨在解释如何从头开始在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时,我只是想把我的逻辑放在一个地方,因为我理解我的组件是视觉上的,每个属性都与每个方法相关联,如果方法分散在不同的服务中,对我来说这样更难读。

最后总结一下,

这篇文章总结了如何创建一个自定义组件,乍一看可能很复杂,但实际上只要计划周全并且理解透彻,你就能轻松地创建你自己的组件。

栈学 🎓

谢谢您看到最后。在您离开前:

0人推荐
随时随地看视频
慕课网APP