在 Angular 中使日期字段具有反应性
Making a date field reactive in Angular
我正在尝试从 ngx-bootstrap
自定义日期字段制作一个 datepicker
组件,以便我可以全球化一些功能和配置。但是我似乎无法在日期输入字段中捕获 Date
对象的值。
我的 date-field.ts
(我正在重新使用文本字段中的一些设置。如果您看到文本字段组件的一些残余,请耐心等待。但我确定我的主要问题是我的组件不知道它是一个日期字段)
import { Component, OnInit, forwardRef, Input } from '@angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'date-field',
templateUrl: './date-field.component.html',
styleUrls: ['./date-field.component.scss'],
providers:[
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DateFieldComponent),
}
]
})
export class DateFieldComponent implements OnInit, ControlValueAccessor {
public dateField = new FormControl("")
private onChange: (name: string) => void;
private onTouched: () => void
@Input() name: string;
@Input() label: string;
@Input() required: boolean;
datepickerConfig = {
dateInputFormat: 'ddd, MMMM Do YYYY',
isAnimated: true,
adaptivePosition: true,
returnFocusToInput: true,
containerClass: 'theme-dark-blue'
}
constructor() { }
ngOnInit() { }
writeValue(obj: any): void {
const date = Date
this.dateField.setValue(new Date());
console.log(date);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
if (isDisabled) {
this.dateField.disable();
} else {
this.dateField.enable();
}
}
doInput() {
this.onChange(this.dateField.value)
}
doBlur() {
this.onTouched();
}
}
模板HTML:
<label
*ngIf="label"
for="{{name}}"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input
type="text"
class="form-control date-field"
#dp="bsDatepicker"
[formControl]="dateField"
(input)="doInput()"
(blur)="doBlur()"
ngModel
bsDatepicker
[bsConfig]="datepickerConfig"
required="{{required}}">
</div>
像这样在父表单中使用它:
<date-field
name="dateChartered"
label="Date local union chartered"
formControlName="dateChartered"
required="true">
</date-field>
<p><strong>Date chartered is:</strong> {{dateChartered}}</p>
假设您在父组件中正确地在控制器中初始化 FormGroup
并在模板中正确使用它,您的组件中有两个主要错误。
首先,正如 Belle Zaid 所说,您应该从自定义日期选择器的 <input>
.
中删除 ngModel
其次,您将 doInput()
绑定到 (input)
,但只有当您在输入字段中键入时才会触发,(change)
也是如此。您应该绑定到 (bsValueChange)
这是 BsDatepicker
公开的输出事件,它更安全,除非您计划更新用户输入的值。
生成的模板将如下所示:
<label *ngIf="label"
for="my-custom-datepicker"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input id="my-custom-datepicker"
type="text"
class="form-control date-field"
required="{{required}}"
bsDatepicker
[formControl]="dateField"
[bsConfig]="datepickerConfig"
(blur)="doBlur()"
(bsValueChange)="doInput()">
</div>
完成这两项更改后,您会注意到控制台中出现错误:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was
checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.. Find more
at https://angular.io/errors/NG0100
这是因为在 writeValue
中调用 this.dateField.setValue(obj)
也会触发 (bsValueChange)
,因此 doInput()
。要解决此问题,您可以按如下方式编辑代码:
private componentInit = false;
// ...
doInput() {
if (!this.componentInit) {
this.componentInit = true;
return;
}
this.onChange(this.dateField.value);
}
我正在尝试从 ngx-bootstrap
自定义日期字段制作一个 datepicker
组件,以便我可以全球化一些功能和配置。但是我似乎无法在日期输入字段中捕获 Date
对象的值。
我的 date-field.ts
(我正在重新使用文本字段中的一些设置。如果您看到文本字段组件的一些残余,请耐心等待。但我确定我的主要问题是我的组件不知道它是一个日期字段)
import { Component, OnInit, forwardRef, Input } from '@angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'date-field',
templateUrl: './date-field.component.html',
styleUrls: ['./date-field.component.scss'],
providers:[
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DateFieldComponent),
}
]
})
export class DateFieldComponent implements OnInit, ControlValueAccessor {
public dateField = new FormControl("")
private onChange: (name: string) => void;
private onTouched: () => void
@Input() name: string;
@Input() label: string;
@Input() required: boolean;
datepickerConfig = {
dateInputFormat: 'ddd, MMMM Do YYYY',
isAnimated: true,
adaptivePosition: true,
returnFocusToInput: true,
containerClass: 'theme-dark-blue'
}
constructor() { }
ngOnInit() { }
writeValue(obj: any): void {
const date = Date
this.dateField.setValue(new Date());
console.log(date);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
if (isDisabled) {
this.dateField.disable();
} else {
this.dateField.enable();
}
}
doInput() {
this.onChange(this.dateField.value)
}
doBlur() {
this.onTouched();
}
}
模板HTML:
<label
*ngIf="label"
for="{{name}}"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input
type="text"
class="form-control date-field"
#dp="bsDatepicker"
[formControl]="dateField"
(input)="doInput()"
(blur)="doBlur()"
ngModel
bsDatepicker
[bsConfig]="datepickerConfig"
required="{{required}}">
</div>
像这样在父表单中使用它:
<date-field
name="dateChartered"
label="Date local union chartered"
formControlName="dateChartered"
required="true">
</date-field>
<p><strong>Date chartered is:</strong> {{dateChartered}}</p>
假设您在父组件中正确地在控制器中初始化 FormGroup
并在模板中正确使用它,您的组件中有两个主要错误。
首先,正如 Belle Zaid 所说,您应该从自定义日期选择器的 <input>
.
ngModel
其次,您将 doInput()
绑定到 (input)
,但只有当您在输入字段中键入时才会触发,(change)
也是如此。您应该绑定到 (bsValueChange)
这是 BsDatepicker
公开的输出事件,它更安全,除非您计划更新用户输入的值。
生成的模板将如下所示:
<label *ngIf="label"
for="my-custom-datepicker"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input id="my-custom-datepicker"
type="text"
class="form-control date-field"
required="{{required}}"
bsDatepicker
[formControl]="dateField"
[bsConfig]="datepickerConfig"
(blur)="doBlur()"
(bsValueChange)="doInput()">
</div>
完成这两项更改后,您会注意到控制台中出现错误:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.. Find more at https://angular.io/errors/NG0100
这是因为在 writeValue
中调用 this.dateField.setValue(obj)
也会触发 (bsValueChange)
,因此 doInput()
。要解决此问题,您可以按如下方式编辑代码:
private componentInit = false;
// ...
doInput() {
if (!this.componentInit) {
this.componentInit = true;
return;
}
this.onChange(this.dateField.value);
}