在 Angular 中使输入禁用反应
Making input disable reactive in Angular
我很难为 Angular 中的输入设置额外的禁用条件。想象一个基本控件:
<input [formControl]="control">
我知道要禁用或启用此输入,通常只需调用 enable()
或 disable()
。
我正在处理一个非常大的代码库。在许多地方 control.disable()
在控件或父控件上调用以强制禁用输入。
我希望在后台加载 API(可能是多个)时暂时禁用输入,即:
<input [formControl]="control" [disabled]="control.disabled || apiLoading()" >
但是 Angular 给了我一个警告,因为它不希望我绑定到 disabled
,而是希望我调用 control.disable()
。这对我来说真的尴尬:
- 每次
apiLoading()
的值更改时我都必须调用 control.disable()/enable()
,这是可能的 via a directive as this blog suggests。
- 但正如我所说,我正在处理一个庞大的代码库。
control.disable()
已经在不可预知的时间到处被调用,并且它具有优先权。每当 apiLoading()
为假时,我都会将控件设置回其最后设置的禁用状态,即 不是 由 apiLoading()
设置的
#2 对我来说真的很难实现。我必须保持最后设置的状态,我通过 control.statusChange
和 emitEvent: false
的组合尝试过,但最终在控制值更改时发出 statusChange
事件时发生冲突。我开始掉入兔子洞。
我只需要一些简单的东西:在 API 加载时临时禁用以应用 在 之上禁用代码库中的其他地方,但因为我被迫要通过 enable()/disable()
指示我的禁用,我必须格外小心,不要撤消已经在我正在处理的庞大代码库中其他地方调用的 disable()
。
在不放弃使用 formControl
(touched/dirty 检测效果很好)的情况下实现类似以下内容的最简单方法是什么?
<input [formControl]="control" [disabled]="control.disabled || apiLoading()" >
apiLoading() 加载应该 return 一个 Observable 然后使用异步管道来消费它:
<input [formControl]="control" [disabled]="control.disabled || apiLoading()|async" >
我找到了一种我并不完全满意但确实有效的方法。使用自定义指令,我可以 no-op 值访问器上的 setDisabledState
,但保留变量中的原始指令。这样我就可以根据我的自定义条件禁用输入 - 而不是必须调用 disable()/enable()
- 并且 formControlDirective
无法控制禁用。
import { FormControlDirective } from "@angular/forms";
import { Directive, Input } from "@angular/core";
/**
* This directive helps to provide a custom method for disabling a form control
* when using the FormControl directive, rather than managing the disabling of
* the control via the enable/disable methods - which can be quite impractical.
*/
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: "[customDisable]",
})
export class CustomDisableDirective {
disabler: (boolean) => void;
@Input() set customDisable(condition: boolean) {
this.disabler(condition);
}
constructor(private dir: FormControlDirective) {
// No-ops the accessor disable so that the formControl directive has
// no effect over the disabling, and disabling is controlled here.
const accessor = this.dir.valueAccessor;
this.disabler = accessor.setDisabledState.bind(accessor);
this.dir.valueAccessor.setDisabledState = () => {};
}
}
然后在 html:
<input [formControl]="control" [customDisable]="control.disabled || apiLoading()" >
我很难为 Angular 中的输入设置额外的禁用条件。想象一个基本控件:
<input [formControl]="control">
我知道要禁用或启用此输入,通常只需调用 enable()
或 disable()
。
我正在处理一个非常大的代码库。在许多地方 control.disable()
在控件或父控件上调用以强制禁用输入。
我希望在后台加载 API(可能是多个)时暂时禁用输入,即:
<input [formControl]="control" [disabled]="control.disabled || apiLoading()" >
但是 Angular 给了我一个警告,因为它不希望我绑定到 disabled
,而是希望我调用 control.disable()
。这对我来说真的尴尬:
- 每次
apiLoading()
的值更改时我都必须调用control.disable()/enable()
,这是可能的 via a directive as this blog suggests。 - 但正如我所说,我正在处理一个庞大的代码库。
control.disable()
已经在不可预知的时间到处被调用,并且它具有优先权。每当apiLoading()
为假时,我都会将控件设置回其最后设置的禁用状态,即 不是 由apiLoading()
设置的
#2 对我来说真的很难实现。我必须保持最后设置的状态,我通过 control.statusChange
和 emitEvent: false
的组合尝试过,但最终在控制值更改时发出 statusChange
事件时发生冲突。我开始掉入兔子洞。
我只需要一些简单的东西:在 API 加载时临时禁用以应用 在 之上禁用代码库中的其他地方,但因为我被迫要通过 enable()/disable()
指示我的禁用,我必须格外小心,不要撤消已经在我正在处理的庞大代码库中其他地方调用的 disable()
。
在不放弃使用 formControl
(touched/dirty 检测效果很好)的情况下实现类似以下内容的最简单方法是什么?
<input [formControl]="control" [disabled]="control.disabled || apiLoading()" >
apiLoading() 加载应该 return 一个 Observable 然后使用异步管道来消费它:
<input [formControl]="control" [disabled]="control.disabled || apiLoading()|async" >
我找到了一种我并不完全满意但确实有效的方法。使用自定义指令,我可以 no-op 值访问器上的 setDisabledState
,但保留变量中的原始指令。这样我就可以根据我的自定义条件禁用输入 - 而不是必须调用 disable()/enable()
- 并且 formControlDirective
无法控制禁用。
import { FormControlDirective } from "@angular/forms";
import { Directive, Input } from "@angular/core";
/**
* This directive helps to provide a custom method for disabling a form control
* when using the FormControl directive, rather than managing the disabling of
* the control via the enable/disable methods - which can be quite impractical.
*/
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: "[customDisable]",
})
export class CustomDisableDirective {
disabler: (boolean) => void;
@Input() set customDisable(condition: boolean) {
this.disabler(condition);
}
constructor(private dir: FormControlDirective) {
// No-ops the accessor disable so that the formControl directive has
// no effect over the disabling, and disabling is controlled here.
const accessor = this.dir.valueAccessor;
this.disabler = accessor.setDisabledState.bind(accessor);
this.dir.valueAccessor.setDisabledState = () => {};
}
}
然后在 html:
<input [formControl]="control" [customDisable]="control.disabled || apiLoading()" >