在 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()。这对我来说真的尴尬:

  1. 每次 apiLoading() 的值更改时我都必须调用 control.disable()/enable(),这是可能的 via a directive as this blog suggests
  2. 但正如我所说,我正在处理一个庞大的代码库。 control.disable() 已经在不可预知的时间到处被调用,并且它具有优先权。每当 apiLoading() 为假时,我都会将控件设置回其最后设置的禁用状态,即 不是 apiLoading() 设置的

#2 对我来说真的很难实现。我必须保持最后设置的状态,我通过 control.statusChangeemitEvent: 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()" >