禁用 Angular 5 输入字段的正确方式

Disable Angular 5 Input fields correct way

我有一个这样创建的 FormGroup:

form: FormGroup;

constructor(private _formBuilder: FormBuilder) { }

this.form = this._formBuilder.group({
  name: ['', Validators.required],
  email: ['', Validators.required, Validators.email]
});

当事件发生时,我想禁用这些输入,因此,在 HTML 我添加了:

<input class="form-control" placeholder="Name" name="name" formControlName="name" [(ngModel)]="name" autocomplete="off" [disabled]="isDisabled" required>

<input class="form-control" placeholder="Email" name="email" formControlName="email" [(ngModel)]="email" email="true" autocomplete="off" [disabled]="isDisabled" required>

其中 isDisabled 是一个变量,我在上述事件发生时切换到 true

如您所想,我收到消息:

It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true when you set up this control in your component class, the disabled attribute will actually be set in the DOM for you. We recommend using this approach to avoid 'changed after checked' errors.

  Example: 
  form = new FormGroup({
    first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

因此,根据此警告显示的示例并进行一些搜索,我发现我应该像这样声明我的控件:

name: [{ value: '', disabled: this.isDisabled }, Validators.required]

问题是:当变量在true/false
之间变化时,它不会在disabled/enabled之间切换
如果启用或禁用输入,使用变量控制的正确方法是什么?

我不想手动执行(例如:this.form.controls['name'].disable()),因为它似乎不是一种非常被动的方式,我必须在大量方法中调用它。可能不是一个好的做法。

感谢

一种解决方案是创建指令并使用绑定,如 here

中所述
import { NgControl } from '@angular/forms';

@Directive({
  selector: '[disableControl]'
})
export class DisableControlDirective {

  @Input() set disableControl( condition : boolean ) {
    const action = condition ? 'disable' : 'enable';
    this.ngControl.control[action]();
  }

  constructor( private ngControl : NgControl ) {
  }

}

然后

<input class="form-control" placeholder="Name" name="name" formControlName="name" autocomplete="off" [disableControl]="isDisabled" required>

注意:

不适用于 Ivy

禁用表单控件的正确方法。对于反应式表单,您永远不应该禁用模板中的输入。因此,在您调用的组件中的任何方法中,您都应该像这样禁用输入:

this.form.get('name').disable();

您可以将变量的赋值更改为 setter 方法,这样您就可以:

set isDisabled(value: boolean) {
 this._isDisabled = value;
 if(value) {
  this.form.controls['name'].disable();
 } else {
    this.form.controls['name'].enable();
  }
}

对于输入使用 [readonly] 而不是 [disabled] 它会起作用

在响应式表单中,您可以通过 this.form.disable() 禁用所有表单字段。 在模板驱动表单中,您可以通过 this.myform.form.disable() 禁用所有表单字段,其中 myForm 是 @ViewChild('form') myForm;

不是我想象的干净或干燥。但是我尝试了 "set method" 并没有开箱即用...

需要一些重构 () => {simpleVersion}(希望对某人有所帮助)

component.ts

...
  // standard stuff...
  form: FormGroup;
  isEditing = false;
...
  // build the form...
  buildForm() {
    this.form = this.FormBuilder.group({
      key: [{value:'locked', disabled: !this.isEditing}],
      name: [],
      item: [],
      active: [false]
    })
  }
  // map the controls to "this" object
  // => i.e. now you can refer to the controls directly (ex. this.yourControlName)
  get key() { return <FormControl>this.form.get('key') }
  get name() { return <FormControl>this.form.get('name') }
...
  // ----------------------------------------
  //     THE GRAND FINALÉ - disable entire form or individual controls
  // ----------------------------------------
  toggleEdit() {
    if(!this.isEditing) {
      this.key.enable();      // controls
      this.name.enable();
      // this.form.enable();     // the form

      this.isEditing = !this.isEditing;
    } else {
      this.key.disable();      // the controls
      this.name.disable();     // the controls

      // this.form.disable();     // or the entire form

      this.isEditing = !this.isEditing;
    }
   }

& 也许在 HTML 逻辑上有点矫枉过正,所以希望您发现集成的 ngClass 切换开关同样有用。

component.html(切换按钮)

<div class="btn-group" (click)="toggleEdit()">
           <label
             class="btn"
             role="button"
             [ngClass]="{'btn-success': isEditing,
                         'btn-warning': !isEditing}">toggle edit
           </label>
</div>

在 Angular 7

中禁用文本框
<div class="center-content tp-spce-hdr">
  <div class="container">
    <div class="row mx-0 mt-4">
      <div class="col-12" style="padding-right: 700px;" >
          <div class="form-group">
              <label>Email</label>
                <input [disabled]="true" type="text" id="email" name="email" 
                [(ngModel)]="email" class="form-control">
          </div>
     </div>
   </div>
 </div>

我有一个功能可以在点击时启用控件。

  controlClick(control: any) {
      this.form.controls[control.ngControl.name].enable();
  }

原来我用的是

  control.disabled = false;

但这不适用于 <input> 的控件,例如在我的 mat-chip-list.

我使用 FormGroup 并在构造函数中禁用每个控件

  constructor(
    private fb: FormBuilder,
    private dialogRef: MatDialogRef<EditDialogComponent>,
    @Inject(MAT_DIALOG_DATA) data
  ) {
    this.data = data;
    this.multiEdit = data.multiSelect;

    this.form = new FormGroup({
      autoArchive: new FormControl({
        value:
          this.getPreFill(data.selectedPolicy.autoArchive, this.multiEdit),
        disabled: true
        /*, Validators.required*/
      }),

...

  <mat-form-field (click)="controlClick(retrieveChipList)">
      <mat-chip-list #retrieveChipList formControlName="retrieveChipList">
        <mat-chip
        *ngFor="let email of data.selectedPolicy.retrieveEmailsToBeNotified" 
          (removed)="remove(email)" [selectable]="selectable"
          [removable]="removable" 
        >
          {{ email }}
          <mat-icon matChipRemove>cancel</mat-icon>
        </mat-chip>
        <input
        placeholder="Retrieve Emails to be Notified" 
        formControlName="retrieveChipList"
          [matChipInputFor]="retrieveChipList"
          [matChipInputAddOnBlur]="true"
          [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
          (matChipInputTokenEnd)="addRetrieveEmails($event)"
        />
      </mat-chip-list>
    </mat-form-field>

您可以在您的 ts 文件中使用此代码。

所有控件:

this.form.disable()
this.form.enable()

一些控件

this.form.get('first').disable()
this.form.get('first').enable()

或初始设置方法。

first: new FormControl({value: '', disabled: true}, Validators.required)

通过创建 directive 并使用 binding 的解决方案在 Angular 10 中对我有用,在 here

中有所描述

模板:

<mat-form-field>
<input matInput class="form-control" formControlName="NameText" [disableControl]="condition" type="text">
</mat-form-field>

打字稿:

import { Directive, Input } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
  selector: '[opDisabled]'
})
export class DisabledDirective {
  @Input()
  set opDisabled(condition: boolean) {
    const action = condition ? 'disable' : 'enable';
    setTimeout(() => this.ngControl.control[action]());
  }

  constructor(private ngControl: NgControl) {}
}

因为无法在 反应式表单 中访问控件。这是由于迁移到 Ivy。您可以使用可以直接访问 html 属性并指定您的条件。有关详细信息和替代方法,请参阅此问题 #35330

[attr.disabled]="true || false"

您可以创建 set 和 get 方法来有条件地为 Angular material 反应式表单实现 enable/disable 功能:

*// 第一步:

 set isDisabled(value:boolean) {
      if(value){
       this.form.controls['Form controller name'].disable(); //you can keep empty if you don't add controller name

      } 
      else{
       this.form.controls['Form controller name'].enable();
      }
    }

// 第二步:在getter

中添加条件
get isDisabled(){
  return condition ? true : false;
}

// 第三步

this.form = this._formBuilder.group({
  name: [{value: '', disabled: this.isDisabled }, [Validators.required]],
  
});