如何以编程方式将属性设置为 FormControl?

How to set attribute to FormControl programmatically?

简短的大问题: 基本上我想要实现的是,将 readonly 属性应用于 FormControl (文本框),就在以编程方式创建它时.

完整场景: 我有一个 Form,它有一个 FormRow,其元素 (FormGroups) 是部分动态生成的。我还提供了一个选项,可以在单击按钮时附加一行。

我希望动态生成的 FormGroupFormControl(文本框)是 readonly,但手动添加的 FormControl 应该是可编辑的。

我怎样才能做到这一点?

<div formArrayName="rows">
    <div class="row" *ngFor="let row of detailForm.get('rows').controls; let i = index" [formGroupName]="i">
        <div class="col-xs-1 text-center">
            <input type="checkbox" [(ngModel)]="detailSelected[i]" [ngModelOptions]="{standalone: true}">
        </div>
        <div class="col-xs-2">
            <input type="text" class="form-control" formControlName="account" (change)="getAccountAssociatedDetails(detailForm.get(['rows', i, 'account']), 'account', i)">
        </div>
        <div class="col-xs-2">
            <input type="text" class="form-control" formControlName="detailNo" (change)="getAccountAssociatedDetails(detailForm.get(['rows', i, 'detailNo']), 'detail', i)">
        </div>
        <div class="col-xs-4">
            <input type="text" class="form-control" readonly formControlName="detailName">
        </div>
        <div class="col-xs-3">
            <input type="text" class="form-control" readonly formControlName="detailCountry">
        </div>
    </div>
</div>

在文本框值 change 上,我正在调用函数 getAccountAssociatedDetails,如您所见。如果有一些与输入的值相关的详细信息,我将替换当前的 FormGroup 并向该行添加更多 FormGroups。

getAccountAssociatedDetails(control: FormControl, type: string, index: number) {
    let value = control.value;
    if (!!value) {
        let form = this.detailForm;
        control.setErrors({
            'alreadyEntered': null
        });

        if (type === 'detail') {
            if (this.detailAlreadyEnteredManually.detailNo.includes(value)) {
                control.setErrors({
                    'alreadyEntered': true
                });
            }
        } else if (type === 'account') {
            if (this.detailAlreadyEnteredManually.accountNumber.includes(value)) {
                control.setErrors({
                    'alreadyEntered': true
                });
            }
        }

        // if detail or account is already entered dont call service
        if (!control.hasError('alreadyEntered')) {
            this.agreementDetailsService.getdetails(value, type)
                .subscribe(response => {
                        let associatedDetailsArray = < Array < any >> response;
                        if (!!associatedDetailsArray) {
                            let rows = < FormArray > form.get('rows');

                            // if search mode is 'detail'
                            if (type === 'detail') {
                                if (!this.detailAlreadyEnteredManually.detailNo.includes(value)) {
                                    rows.setControl(index, this.builddetailItem(associatedDetailsArray[0].accountNumber.value, value, associatedDetailsArray[0].detailName, associatedDetailsArray[0].detailCountry));
                                    this.detailAlreadyEnteredManually.detailNo.push(value);

                                    for (let i = 1; i < associatedDetailsArray.length; i++)
                                        rows.insert(index + i, this.builddetailItem(associatedDetailsArray[i].accountNumber.value, value, associatedDetailsArray[i].detailName, associatedDetailsArray[i].detailCountry));
                                }

                                //if search mode is 'account'
                            } else if (type === 'account') {
                                if (!this.detailAlreadyEnteredManually.accountNumber.includes(value)) {
                                    rows.setControl(index, this.builddetailItem(value, associatedDetailsArray[0].detailNo.value, associatedDetailsArray[0].detailName, associatedDetailsArray[0].detailCountry));
                                    this.detailAlreadyEnteredManually.accountNumber.push(value);

                                    for (let i = 1; i < associatedDetailsArray.length; i++)
                                        rows.insert(index + i, this.builddetailItem(value, associatedDetailsArray[i].detailNo.value, associatedDetailsArray[i].detailName, associatedDetailsArray[i].detailCountry));
                                }
                            }
                        }
                    },
                    error => console.log(error));
        }
    }
}

FormGroup 中的最后两个字段默认为 readonly。一旦用户在前两个字段中的任何一个输入内容并聚焦,就应该获取与该值相关的详细信息,替换当前的 FormGroup 并附加生成列表中的其他 FormGroup。我希望那些新添加的 FormGroup 中的所有 FormControl 都应该是 readonly。用户可以添加更多字段并重复该过程。

据此:https://github.com/angular/angular/issues/11447,无法在表单控件上添加 readonly,仅支持 disabled

我什至更喜欢 disabled,因为它在视觉上(灰色)显示用户无法触摸它。所以当你想禁用你的表单控件时,你只需使用:

formControlNameHere.disable();

或者如果您需要禁用整个表单组,您可以使用

以相同的方式进行
myFormGroupNameHere.disable()

我们需要记住的是,此表单控件随后从表单对象中排除。要获取所有禁用的值,您需要调用:

this.myForm.getRawValue();

如果您确实想添加 readonly,则需要对其进行一些变通::

[readonly]="anyBooleanPropertyFromComponent" 

当然在那个答案中我们使用了一个布尔值,这当然对你不起作用,然后我看到你需要设置一个额外的布尔 formcontrol,然后你可以用 [readonly]。但这只是在您的表单组中添加一个新的 "unnecessary" 属性,所以使用 disabled 的另一个原因 :)

希望对您有所帮助!