如何在 Angular 2 NgForm 上观察触摸事件?
How to observe touched event on Angular 2 NgForm?
可以订阅 NgForm
的 valueChanges
可观察对象 属性 的回调,以便对表单控件值的变化做出反应。
我需要以同样的方式对用户触摸表单控件之一的事件做出反应。
This class 似乎定义了 valueChanges
Observable 而 touched
属性 被定义为布尔值。
有没有办法对 "control touched" 事件做出反应?
ng2 没有提供对触摸事件做出反应的直接方式。它使用 (input) 事件触发 valueChanges 事件和 (blur) 事件来设置 [= AbstractControl 的 12=]touched/untouched 属性。
所以你需要在模板中手动订阅所需的事件并在你的组件中处理它 class.
您可以扩展默认 FormControl
class,并添加 markAsTouched
将调用本机方法的方法,以及您的副作用。
import { Injectable } from '@angular/core';
import { FormControl, AsyncValidatorFn, ValidatorFn } from '@angular/forms';
import { Subscription, Subject, Observable } from 'rxjs';
export class ExtendedFormControl extends FormControl {
statusChanges$: Subscription;
touchedChanges: Subject<boolean> = new Subject<boolean>();
constructor(
formState: Object,
validator: ValidatorFn | ValidatorFn[] = null,
asyncValidator: AsyncValidatorFn | AsyncValidatorFn[] = null
) {
super(formState, validator, asyncValidator);
this.statusChanges$ = Observable.merge(
this.valueChanges,
this.touchedChanges.distinctUntilChanged()
).subscribe(() => {
console.log('new value or field was touched');
});
}
markAsTouched({ onlySelf }: { onlySelf?: boolean } = {}): void {
super.markAsTouched({ onlySelf });
this.touchedChanges.next(true);
}
}
如果您的问题与我的有任何相似之处,我试图在一个组件中将某个字段标记为已触及,然后在另一个组件中对其做出响应。我可以访问该字段的 AbstractControl
。我绕过它的方法是
field.markAsTouched();
(field.valueChanges as EventEmitter<any>).emit(field.value);
然后我刚刚在我的其他组件中订阅了 valueChanges。值得注意的是:field.valueChanges
被导出为一个 Observable,但在运行时它是一个 EventEmitter
,这使得这个解决方案不太漂亮。另一个限制显然是您订阅的不仅仅是触摸状态。
有同样的问题 - 将此辅助方法放在一起以提取一个可观察对象,您可以在表单中订阅该对象,以便在触摸状态发生变化时收到通知:
// Helper types
/**
* Extract arguments of function
*/
export type ArgumentsType<F> = F extends (...args: infer A) => any ? A : never;
/**
* Creates an object like O. Optionally provide minimum set of properties P which the objects must share to conform
*/
type ObjectLike<O extends object, P extends keyof O = keyof O> = Pick<O, P>;
/**
* Extract a touched changed observable from an abstract control
* @param control AbstractControl like object with markAsTouched method
*/
export const extractTouchedChanges = (control: ObjectLike<AbstractControl, 'markAsTouched' | 'markAsUntouched'>): Observable<boolean> => {
const prevMarkAsTouched = control.markAsTouched.bind(control);
const prevMarkAsUntouched = control.markAsUntouched.bind(control);
const touchedChanges$ = new Subject<boolean>();
function nextMarkAsTouched(...args: ArgumentsType<AbstractControl['markAsTouched']>) {
prevMarkAsTouched(...args);
touchedChanges$.next(true);
}
function nextMarkAsUntouched(...args: ArgumentsType<AbstractControl['markAsUntouched']>) {
prevMarkAsUntouched(...args);
touchedChanges$.next(false);
}
control.markAsTouched = nextMarkAsTouched;
control.markAsUntouched = nextMarkAsUntouched;
return touchedChanges$;
}
// Usage (in component file)
...
this.touchedChanged$ = extractTouchedChanges(this.form);
...
然后我想做 merge(this.touchedChanged$, this.form.valueChanges)
来观察更新验证所需的所有更改。
*编辑 - 根据@marked-down的建议,我已将对前一个函数的调用移动到发出新值之前,以防您在收到值后直接查询并最终退出同步
我是这样解决的:
this.control['_markAsTouched'] = this.control.markAsTouched;
this.control.markAsTouched = () => {
this.control['_markAsTouched']();
// your event handler
}
基本上我正在覆盖 FormControl
的默认 markAsTouched
方法。
可以订阅 NgForm
的 valueChanges
可观察对象 属性 的回调,以便对表单控件值的变化做出反应。
我需要以同样的方式对用户触摸表单控件之一的事件做出反应。
This class 似乎定义了 valueChanges
Observable 而 touched
属性 被定义为布尔值。
有没有办法对 "control touched" 事件做出反应?
ng2 没有提供对触摸事件做出反应的直接方式。它使用 (input) 事件触发 valueChanges 事件和 (blur) 事件来设置 [= AbstractControl 的 12=]touched/untouched 属性。 所以你需要在模板中手动订阅所需的事件并在你的组件中处理它 class.
您可以扩展默认 FormControl
class,并添加 markAsTouched
将调用本机方法的方法,以及您的副作用。
import { Injectable } from '@angular/core';
import { FormControl, AsyncValidatorFn, ValidatorFn } from '@angular/forms';
import { Subscription, Subject, Observable } from 'rxjs';
export class ExtendedFormControl extends FormControl {
statusChanges$: Subscription;
touchedChanges: Subject<boolean> = new Subject<boolean>();
constructor(
formState: Object,
validator: ValidatorFn | ValidatorFn[] = null,
asyncValidator: AsyncValidatorFn | AsyncValidatorFn[] = null
) {
super(formState, validator, asyncValidator);
this.statusChanges$ = Observable.merge(
this.valueChanges,
this.touchedChanges.distinctUntilChanged()
).subscribe(() => {
console.log('new value or field was touched');
});
}
markAsTouched({ onlySelf }: { onlySelf?: boolean } = {}): void {
super.markAsTouched({ onlySelf });
this.touchedChanges.next(true);
}
}
如果您的问题与我的有任何相似之处,我试图在一个组件中将某个字段标记为已触及,然后在另一个组件中对其做出响应。我可以访问该字段的 AbstractControl
。我绕过它的方法是
field.markAsTouched();
(field.valueChanges as EventEmitter<any>).emit(field.value);
然后我刚刚在我的其他组件中订阅了 valueChanges。值得注意的是:field.valueChanges
被导出为一个 Observable,但在运行时它是一个 EventEmitter
,这使得这个解决方案不太漂亮。另一个限制显然是您订阅的不仅仅是触摸状态。
有同样的问题 - 将此辅助方法放在一起以提取一个可观察对象,您可以在表单中订阅该对象,以便在触摸状态发生变化时收到通知:
// Helper types
/**
* Extract arguments of function
*/
export type ArgumentsType<F> = F extends (...args: infer A) => any ? A : never;
/**
* Creates an object like O. Optionally provide minimum set of properties P which the objects must share to conform
*/
type ObjectLike<O extends object, P extends keyof O = keyof O> = Pick<O, P>;
/**
* Extract a touched changed observable from an abstract control
* @param control AbstractControl like object with markAsTouched method
*/
export const extractTouchedChanges = (control: ObjectLike<AbstractControl, 'markAsTouched' | 'markAsUntouched'>): Observable<boolean> => {
const prevMarkAsTouched = control.markAsTouched.bind(control);
const prevMarkAsUntouched = control.markAsUntouched.bind(control);
const touchedChanges$ = new Subject<boolean>();
function nextMarkAsTouched(...args: ArgumentsType<AbstractControl['markAsTouched']>) {
prevMarkAsTouched(...args);
touchedChanges$.next(true);
}
function nextMarkAsUntouched(...args: ArgumentsType<AbstractControl['markAsUntouched']>) {
prevMarkAsUntouched(...args);
touchedChanges$.next(false);
}
control.markAsTouched = nextMarkAsTouched;
control.markAsUntouched = nextMarkAsUntouched;
return touchedChanges$;
}
// Usage (in component file)
...
this.touchedChanged$ = extractTouchedChanges(this.form);
...
然后我想做 merge(this.touchedChanged$, this.form.valueChanges)
来观察更新验证所需的所有更改。
*编辑 - 根据@marked-down的建议,我已将对前一个函数的调用移动到发出新值之前,以防您在收到值后直接查询并最终退出同步
我是这样解决的:
this.control['_markAsTouched'] = this.control.markAsTouched;
this.control.markAsTouched = () => {
this.control['_markAsTouched']();
// your event handler
}
基本上我正在覆盖 FormControl
的默认 markAsTouched
方法。