如何在 Angular 2 NgForm 上观察触摸事件?

问题描述 投票:0回答:7

可以订阅

NgForm
valueChanges
可观察属性的回调,以便对表单控件值的更改做出反应。

我需要以同样的方式对用户触摸表单控件之一的事件做出反应。

这个类似乎定义了 valueChanges

 Observable 并且 
touched
 属性被定义为布尔值。

有没有办法对“控制触摸”事件做出反应?

angular typescript angular2-forms
7个回答
23
投票
您可以扩展默认的

FormControl

 类,并添加将调用本机方法的 
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); } }
    

17
投票
ng2 没有提供直接的方法来对触摸事件做出反应。它使用 (

input) 事件来触发 valueChanges 事件,并使用 (blur) 事件来设置 AbstractControltouched/untouched 属性。 因此,您需要在模板中手动订阅所需的事件并在组件类中处理它。


13
投票
有同样的问题 - 组合这个辅助方法来提取一个可观察的对象,您可以在表单中订阅该可观察对象,以便在触摸状态更改时收到通知:

// 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的建议,我将对前一个函数的调用移至发出新值之前,以防您在收到值后直接查询并最终不同步


4
投票
如果您的问题与我的问题类似,我会尝试将一个组件中的某个字段标记为已触摸,然后在另一个组件中对此做出响应。我可以访问该字段的

AbstractControl

。我解决这个问题的方法是

field.markAsTouched(); (field.valueChanges as EventEmitter<any>).emit(field.value);
然后我只是在其他组件中订阅了 valueChanges。值得注意的是:

field.valueChanges

被导出为一个Observable,但在运行时它是一个
EventEmitter
,这使得这是一个不太漂亮的解决方案。另一个限制显然是您订阅的不仅仅是触摸状态。


4
投票
我已经这样解决了:

this.control['_markAsTouched'] = this.control.markAsTouched; this.control.markAsTouched = () => { this.control['_markAsTouched'](); // your event handler }
基本上我正在覆盖 

markAsTouched

 的默认 
FormControl
 方法。


0
投票
这是我想出的 util 函数,它还监听

reset

 方法并使控件保持不变:

/** * Allows to listen the touched state change. * The util is needed until Angular allows to listen for such events. * Https://github.com/angular/angular/issues/10887. * @param control Control to listen for. */ export function listenControlTouched( control: AbstractControl, ): Observable<boolean> { return new Observable<boolean>(observer => { const originalMarkAsTouched = control.markAsTouched; const originalReset = control.reset; control.reset = (...args) => { observer.next(false); originalReset.call(control, ...args); }; control.markAsTouched = (...args) => { observer.next(true); originalMarkAsTouched.call(control, ...args); }; observer.next(control.touched); return () => { control.markAsTouched = originalMarkAsTouched; control.reset = originalReset; }; }); }
    

0
投票

@ʞᴉɯ发布的扩展解决方案

const form = new FormControl(''); (form as any)._markAsTouched = form.markAsTouched; (form as any).touchedChanges = new Subject(); form.markAsTouched = opts => { (form as any)._markAsTouched(opts); (form as any).touchedChanges.next('touched'); }; ... (form as any).touchedChanges.asObservable().subscribe(() => { // execute something when form was marked as touched });
    
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.