简体   繁体   中英

Disable Angular 5 Input fields correct way

I have a FormGroup that was created like that:

form: FormGroup;

constructor(private _formBuilder: FormBuilder) { }

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

When an event occurs I want to disable those inputs, so, in the HTML I added:

<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>

Where isDisabled is a variable I toggle to true when the said event happens.

As you can imagine, I get the message:

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) });

So, with the example this warning shows and with a little search I found that I should declare my controls like:

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

The problem is: It is not toggling between disabled/enabled when the variable changes between true / false

How is the correct way of having a variable controlling if an input is enabled or disabled?

I don't want to do it manually (ex: this.form.controls['name'].disable() ) because it doesn't seems a very reactive way, I would have to call it inside a good amount of methods. Probably not a good practice.

Thx

You can change the assignment of the variable to a setter method so that you'd have:

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

One solution is creating a directive and using binding for that as described in 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 ) {
  }

}

then

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

NOTE:

Doesn't work with Ivy

The proper way to disable an form control. With reactive forms you should never disable an input from the template. So in whatever method in your component you are calling you should disable the input like this:

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

Disable TextBox in 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>

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

You can use this code on your ts file.

All controls:

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

Some controls

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

Or Initial set method.

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

In Reactive Form you can disable all form fields by this.form.disable() . In Template Driven Form you can disable all form fields by this.myform.form.disable() where myForm is @ViewChild('form') myForm ;

Not the clean or dry'st I imagine. Bu I tried the "set method" and didn't work out of the box...

Needed some refactoring () => {simpleVersion} (hope it helps someone)

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;
    }
   }

& perhaps overkill on the HTML logic, so hope you find the bonus integrated ngClass toggle just as helpful.

component.html (toggle button)

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

The solution by creating a directive and using binding for that worked for me in Angular 10 is described in here

Template:

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

TypeScript:

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) {}
}

I have a function that enables a control on click.

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

Originally i was using

  control.disabled = false;

But this did not work for controls with <input> for example in my mat-chip-list .

I use FormGroup and disable each control in the constructor

  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>

As control can't be accessed in reactive forms . This is due to migration to Ivy . You can use can access the html attribute directly and specify your condition. See this issue #35330 for more details and alternative methods.

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

You can create set and get method to achieve conditionally enable/disable functionality for Angular material Reactive Forms:

*// 1st Step:

 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();
      }
    }

// 2nd Step: Add conditions in getter

get isDisabled(){
  return condition ? true : false;
}

// 3rd Step

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

Remove [disabled]="isDisabled" from input fields and add ng-disabled="all" and in the event field add ng-model="all"

<body ng-app="">
Click here to disable all the form fields:<input type="checkbox" ng-model="all">


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

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

</body>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM