“简要”解释一下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