季度日期选择器
Quarterly datepicker
我正在寻找使用 ng-bootstrap 的季度日期选择器。
目前我有月份和年份,请参阅 STACKBLITZ,但我想将月份更改为季度。
这可以用 ng-bootstrap 实现吗?
For info: here is a previous stackblitz example using Angular Material
“简要”解释一下 stackblitz
基本上我们有一个 ng-dropDown 显示一系列 ngbDropdownItem
我们有变量
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>
此外,我们还展示了一个“header”,它有两个按钮(左箭头和右箭头)和一个显示年或十年的跨度
<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”做出一个或另一个动作
函数比较简单,再次考虑到一开始year和quarter是没有值的,那我们就用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 以改进和删除不必要的样式
Update 在 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 订阅和点击功能中调用
离开
注意:像 ng-bootstrap 我选择 return 值是 object 属性年和季度
Update @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
我正在寻找使用 ng-bootstrap 的季度日期选择器。
目前我有月份和年份,请参阅 STACKBLITZ,但我想将月份更改为季度。
这可以用 ng-bootstrap 实现吗?
For info: here is a previous stackblitz example using Angular Material
“简要”解释一下 stackblitz
基本上我们有一个 ng-dropDown 显示一系列 ngbDropdownItem
我们有变量
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>
此外,我们还展示了一个“header”,它有两个按钮(左箭头和右箭头)和一个显示年或十年的跨度
<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”做出一个或另一个动作
函数比较简单,再次考虑到一开始year和quarter是没有值的,那我们就用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 以改进和删除不必要的样式
Update 在 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 订阅和点击功能中调用
离开注意:像 ng-bootstrap 我选择 return 值是 object 属性年和季度
Update @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