【问题标题】:Quarterly datepicker季度日期选择器
【发布时间】:2020-02-07 19:38:12
【问题描述】:

我正在寻找一个使用 ng-bootstrap 的季度日期选择器。

目前我有月份和年份,请参阅 STACKBLITZ,但我想将月份更改为季度。

ng-bootstrap 可以做到吗?

供参考:这是之前使用 Angular Material

的 stackblitz 示例

【问题讨论】:

  • jsfiddle.net/76Lhvfy0 这个例子显示了一个季度日期选择器,但不幸的是它在 jQuery @malbarmawi 中
  • 我在stackblitz.com/edit/angular-zuyj77?file=src 中留下了一个正在进行的工作和“原样”注意:我会尝试改进,但我不确定我什么时候会有时间(并解释代码)。基本上是一个带有 auto-close="outside" 的 ng-dropdown。我复制了箭头的css,祝你好运

标签: angular bootstrap-4 datepicker ng-bootstrap


【解决方案1】:

“简要”解释一下stackblitz

基本上我们有一个显示一系列 ngbDropdownItem 的 ng-dropDown

我们有变量

year:number; //the year selected
quarter:string; //hte quarter selected

yearDefault=new Date().getFullYear() //the year by defect
quarterDefault="Q"+(1+Math.floor(new Date().getMonth()/3)) //the quarter by defect

showQuarter:boolean=true; //a boolean variable. 
         //if true, the ngbDropdownItems will be the quarter, 
         //else the years

year10:number; //auxliar for show the list of years

还有一个显示季度的辅助数组

options: any[] = [
    {value:'Q1',months:['Jan','Feb','Mar']},
    {value:'Q2',months:['Apr','May','Jun']},
    {value:'Q3',months:['Jul','Aug','Sep']},
    {value:'Q4',months:['Oct','Nov','Dec']}
  ];

所以,我们可以在 ngbDropdownMenu 中显示

<ng-container *ngIf="showQuarter">
    <button [ngClass]="{'bg-primary':item.value==quarter}" ngbDropdownItem 
         *ngFor="let item of options" 
         (click)="click(item.value,drop)">
        <span class="col" *ngFor="let month of item.months" >
           {{month}}
        </span>
    </button>
</ng-container>

//or

<ng-container *ngIf="!showQuarter">
    <button [ngClass]="{'bg-primary':(year10+item)==year}" ngbDropdownItem 
          *ngFor="let item of [0,1,2,3,4,5,6,7,8,9]" 
          (click)="changeYear(year10+item);showQuarter=true">
        <span >{{year10+item}}</span>
    </button>
</ng-container>

此外,我们显示一个带有两个按钮(左箭头和右箭头)的“标题”和一个显示年份或十年的跨度

<div class="selectYear">
    <div class="ngb-dp-arrow">
        <button  class="btn btn-link ngb-dp-arrow-btn" type="button"
            (click)="showQuarter?changeYear((year||yearDefault)-1):year10=year10-10">
           <span class="ngb-dp-navigation-chevron">
            </span>
        </button>
    </div>
    
    <button type="button" class="btn btn-link" (click)="changeShowQuarter()">
        {{showQuarter?year?year:yearDefault:(year10+' - '+(year10+9))}}
    </button>
    
    <div class="ngb-dp-arrow right">
        <button class="btn btn-link ngb-dp-arrow-btn" type="button" 
            (click)="showQuarter?changeYear((year||yearDefault)+1):year10=year10+10">
            <span class="ngb-dp-navigation-chevron">
            </span>
        </button>
    </div>
</div>

查看按钮如何根据变量“showQuarter”执行一项或另一项操作

函数很简单,我们再次考虑到一开始年和季度可以没有值,在这种情况下我们使用yearDefault和QuarterDefault

changeYear(year)
  {
    this.year=year || this.yearDefault;
    this.quarter=this.quarter||this.quarterDefault
              this.control.setValue(this.quarter+" "+this.year || this.yearDefault,{emitEvent:false})
  }
  changeShowQuarter()
  {
    this.showQuarter=!this.showQuarter
    if (!this.showQuarter)
      this.year10=this.year?10*Math.floor(this.year/10):10*Math.floor(this.yearDefault/10)
  }
  click(quarter,drop)
  {
    this.quarter=quarter;
    this.year=this.year||this.yearDefault
              this.control.setValue(this.quarter+" "+this.year,{emitEvent:false})
    drop.close()
  }

是的,我们有一个名为 control 的 FormControl,因为我们有一个输入

  <input style="text-transform: uppercase" [formControl]="control" placeholder="Qq yyyy" >
  
  control:FormControl= new FormControl()

为了控制季度和年份的手动输入,我们订阅了 control.valueChanges,仅当字符串长度大于或等于 6 时才赋予年份和季度值

this.control.valueChanges.pipe(
      takeWhile(()=>this.alive),
      startWith(this.quarter+" "+this.year),
      debounceTime(200))
    .subscribe((res:string)=>{
//      console.log(this.controlID.nativeElement.selectionStart)
       if (res)
       {
         res=res.toUpperCase()
         if (res[0]!="Q")
           res="Q"+res;
           let value=res.replace(/[^Q|0-9]/g, '');
           let quarter;
           let year;
           if (value.length>=2)
              quarter=value[0]+value[1]
           if (value.length>=6)
           {
              year=value.substr(2,4)
              this.year=+year
              this.quarter=quarter;
              this.control.setValue((this.quarter+" "+this.year),{emitEvent:false})
           }
           else
           {
             this.year=null;
             this.quarter=null;
           }
       }
    })

TODO:使用组件创建自定义表单控件,修改 .css 以改进和删除不必要的样式

更新在CustomFormControl中转换很简单,只实现ControlValueAccessor

  disabled: boolean = false;
  onChange: (_: any) => void;
  onTouched: any;

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

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

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
  writeValue(value: any): void {
    if (value) {
      this.year = value.year;
      this.quarter = value.quarter;
      if (this.year && this.quarter)
        this.control.setValue(this.quarter + " " + this.year,{emitEvent:false});
    }
  }

我使用辅助功能

  setValue(data: any) {
      if (data && data.quarter && data.year) {
        this.control.setValue(data.quarter + " " + data.year, {
          emitEvent: false
        });
        this.onChange({ quarter: data.quarter, year: data.year });
      } else {
        this.control.setValue(data, { emitEvent: false });
        this.onChange(null);
      }
  }

control.valueChanges 订阅和点击函数中的调用

我离开this stackblitz

注意:与 ng-bootstrap 一样,我选择返回值是具有属性 year 和 Quarter 的对象

更新 @Mohan 询问有关改进月份日期选择器添加 minQuarter 的问题。

要添加 minQuarter 和 maxQuarter,“仅”添加两个输入

@Input() minQuarter={ quarter: "Q1", year: 0 };
@Input() maxQuarter={ quarter: "Q4", year: 9999 }

好吧,现在我们需要禁用按钮来考虑这个值。有几个按钮,禁用时需要注意

//arrow left
[disabled]="minQuarter.year>=(showQuarter?year:year10)"

//arrow right
[disabled]="maxQuarter.year<=(showQuarter?year:(year10+10))" 

//the quarters
[disabled]="(minQuarter.year==year && item.value<minQuarter.quarter) 
          ||(maxQuarter.year==year && item.value>minQuarter.quarter)"

//the years
[disabled]="((year10+item)<minQuarter.year || (year10+item)>maxQuarter.year)"

此外,我们可以在手动更改季度时考虑,强制获取 minQuarter 或 maxQuarter。所以在订阅control.valueChanges,

this.control.valueChanges
  .pipe(...)
  .subscribe((res: string) => {
    let quarter = null;
    let year = null;
        ...
    //here check the min and max value
    if (year)
    {
       if (year<this.minQuarter.year)
           year=this.minQuarter.year

       if (year>this.minQuarter.year)
           year=this.maxQuarter.year

       if (year==this.minQuarter.year && quarter<this.minQuarter.quarter)
           quarter=this.minQuarter.quarter;

       if (year==this.maxQuarter.year && quarter>this.minQuarter.quarter)
           quarter=this.maxQuarter.quarter;
    }
    ....
  });

}

如果我没有错过,那就是全部。我更新了stackblitz

【讨论】:

  • 以上更改有效。请建议我如何禁用现有的季度月。示例:当月是 2020 年 9 月。所以我应该只启用当月季度(用户不应选择上月季度)。
  • 我更新了答案和堆栈闪电战。我觉得还可以
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
相关资源
最近更新 更多