Reputation: 10009
I'm using matInput
and mat-form-field
(@angular/material) in an Angular component, and I can't disable the matInput
.
A working example can be seen here.
It seems likely that I'm missing something obvious, but for the life of me I can't figure out what. Is this a bug?
If I remove [formControlName]
from the CustomFormInputComponent
, then I can successfully disable the matInput
CustomFormInputComponent
:
import { Input, Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-custom-form-input',
template: `
<mat-form-field [formGroup]="form">
<input matInput placeholder='Name' [formControlName]="formControlName" [disabled]='disabled'>
</mat-form-field>
`,
})
export class CustomFormInputComponent {
@Input() form: FormGroup;
@Input() formControlName: string = 'name';
@Input() disabled = false;
}
AppComponent
:
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'my-app',
template: `
<p>At least one of these inputs should be disabled, but none are :(</p>
<app-custom-form-input [form]="form" [disabled]='true'></app-custom-form-input>
<app-custom-form-input [form]="form" [disabled]="'disabled'"></app-custom-form-input>
`,
})
export class AppComponent {
public form: any;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.form = this.fb.group({
name: ''
})
}
}
Any insights are greatly appreciated!
For a bit more context on David's answer: Angular updates DOM state based on the disabled status of a reactive form control. What I think is happening: angular is rendering the CustomFormInputComponent
before the AppComponent
and is rendering the component as disabled. Then the AppComponent is rendered and the form
is built with the name
control enabled. Angular then goes and un-disabled the DOM input element (which is behavior as designed).
Upvotes: 55
Views: 134886
Reputation: 659
<mat-form-field fxFlex>
<input matInput placeholder="No" formControlName="no" readonly>
</mat-form-field>
Have you tried readonly option. This works fine with me.
Working code: html:
<mat-form-field fxFlex>
<input matInput placeholder="No" formControlName="no" readonly>
</mat-form-field>
In case you need to bind it to a disabled
property of your component
<input matInput [readonly]="disabled" (keyup)="search()"
[formControl]="..." class="...">
Upvotes: 46
Reputation: 972
I hade the same issue and already know the other options. But all of them had at least one issue:
1)
<input [formControl]="form" readonly>
// not working with binding. You have to decide it once at first initiating
this.form.disable();
// I have to subscribe on variable changes to trigger enable/disable method
new FormControl({ value: '', disabled: this.disable });
// Again, it is not enable/disable on variable change
<mat-form-field *ngIf="disabled">...</mat-form-field>
<mat-form-field *ngIf="!disabled">...</mat-form-field>
// It is working! But on common changes these doms will everytime fully initialized
So I decided to use the following solution:
<mat-form-field [class.disabled]="disable">...</mat-form-field>
<style>
.disabled {
pointer-events: none;
color: rgba(0, 0, 0, 0.38);
}
</style>
By this the user is not able to interact with the input anymore. And the style is the same like on disabled.
Of course someone can remove the class by the developer bar, but this is also possible with disabled attribute. So in any case, you should also protect the JavaScript logic behind the input.
Upvotes: 2
Reputation: 11
While I agree that using readonly
is the easiest way forward, it's often not clear to a user that the read only element is "off-limits". If you truly want the "normal" disabled look/behavior on the HTML form, but still want/need the value to be included in the form submission, include a hidden form input that Angular will control, along with the visible and disabled input that displays a copy of the hidden value...best of both worlds? This seemed easier than using read only but trying to style the control to look disabled.
In this example, I want the internal id
to display and clearly be disabled, but still need it included in the form submission.
<input type="hidden" formControlName="id">
<input disabled="" [value]="theId">
The input will look and behave like a disabled control, but any submission will include the hidden value.
Upvotes: 0
Reputation: 338
Everyone reading this needs to be aware of the following:
Disable() method will remove the value from your from. Debug your form Group, and look at what happens when you use .disable() on an input. You will have a FormControl still, but the value for that particular input will be gone. Look at FormGroup.value when you use enable()/disable().
The readonly answer above is by far the easiest way to implement disabling an input that you still need a value from
Using a combination of readonly and ngIf/ng-templates you will be able to successfully toggle a disabled input and still capture it's value in your form.
Disabling a reactive form in the DOM simply doesn't work like you think it should.
When you disable an input during the FormControl creation, it doesn't matter if you bind the disabled property to a variable that you are going to try and manipulate later. It only takes a snapshot of that properties value at the time of creation and sets disabled as that boolean value on the control. Try and manipulate that value all you want, the control has already been created and doesn't care what the new value is.
An example using the readonly answer above, and ng-templates:
<div class="col-4">
<div *ngIf="hasVarHeight.checked else normalHeight">
<mat-form-field>
<mat-label>Height</mat-label>
<input
type="text"
matInput
[formControl]="heightCtrl"
[value]="'*'" readonly>
</mat-form-field>
</div>
<ng-template #normalHeight>
<mat-form-field>
<mat-label>Height</mat-label>
<input
type="text"
matInput
[formControl]="heightCtrl"
mask="separator.3"
thousandSeparator=","
[suffix]="unitCtrl.value === 'Metric' ? ' mm' : ' in'"
[placeholder]="unitCtrl.value === 'Metric' ? 'mm' : 'inches'"
[dropSpecialCharacters]="false">
<mat-error *ngIf="heightCtrl.hasError('required')">
Height is required
</mat-error>
</mat-form-field>
</ng-template>
</div>
</div>
<div class="form-row">
<div class="col-4">
<mat-checkbox
#hasVarHeight
(click)="makeVariable('height')"
name="varheight"
[formControl]="hasVarHeightCtrl">
Variable Height?
</mat-checkbox>
</div>
</div>
Hopefully the above gives you an idea about how you can use readonly and templates to make your form better, and still retain masks on inputs that you require. You can use a method like makeVariable() above to dynamically set a value on a readonly input, and present the user with something useful as well.
Just a thought. Thank you for the readonly answer Yogesh.
Upvotes: 1
Reputation: 744
If you are using FormGroup you have to use disabled property creating your FormGroup/FormControl:
name: new FormControl({ value: '', disabled: this.disabled })
But if you want to disable/enable you can use this in your HTML:
<input type="text" formControlName="name" [attr.disabled]="isDisabled == true ? true : null" />
Upvotes: 6
Reputation: 11
In the html page:
<form [formGroup]="formForAddDSBData">
<mat-form-field class="form-element" >
<mat-icon matPrefix><i class="fa fa-address-card fa-1x"></i></mat-icon>
<input matInput disabled id="AreaAddress" formControlName="AreaAddress"
#Contact1 placeholder="Area/Address" type="text" >
<mat-error *ngIf="!formForAddDSBData.controls['AreaAddress'].valid &&
formForAddDSBData.controls['AreaAddress'].touched">
Area/Address is required
</mat-error>
</mat-form-field>
</form>
to disable this mat-input you need to write the below code in .ts file
this.formForAddDSBData.get("AreaAddress").disable();
Upvotes: -2
Reputation: 17
If You are using material then you can add
<mat-form-field [formGroup]="form">
<input matInput placeholder='Name' [formControlName]="formControlName" readOnly>
</mat-form-field>
Upvotes: 0
Reputation: 1235
I have had the same issue and I have solved it with *ngIf directive. If input should be disabled, make it disabled, remove its form binding and give its value manually. If it's not, than use your formControl as it is.
This is your template:
<mat-form-field [formGroup]="form">
<input matInput placeholder='Name' [formControlName]="formControlName" [disabled]='disabled'>
</mat-form-field>
change it with:
<mat-form-field *ngIf="disabled">
<input matInput placeholder='Name' [value]="form.formControlName" disabled='true'>
</mat-form-field>
<mat-form-field *ngIf="!disabled" [formGroup]="form">
<input matInput placeholder='Name' [formControlName]="formControlName">
</mat-form-field>
Upvotes: 9
Reputation: 748
-->output try this.
.html file
<form name="fg" [formGroup]="fg" >
<mat-form-field >
<input matInput placeholder="Email" formControlName="email">
</mat-form-field>
</form>
.ts file
import this : import { FormBuilder, FormGroup, Validators } from '@angular/forms';
constructor(private _formBuilder: FormBuilder) { }
this.fg= this._formBuilder.group({
email :[
{
value : '[email protected]',
disabled: true
},
Validators.required
],
Upvotes: 8
Reputation: 4890
If you are using a FormGroup, then you should not disable the form in the HTML Template. It will not work if you try to disable in HTML together with FormControl. Instead, it should be done within the FormGroup. Try this:
template: `
<mat-form-field [formGroup]="form">
<input matInput placeholder='Name' [formControlName]="formControlName">
</mat-form-field>
`
and:
ngOnInit() {
this.form = this.fb.group({
name: new FormControl({ value: '', disabled: this.disabled })
});
}
Also...not a big deal but..you should really be doing:
public form: FormGroup;
instead of:
public form: any;
Don't forget the import as well
import { FormGroup, FormControl } from '@angular/forms';
Btw, the name inside of the form group declaration should match whatever you have set in the HTML. So:
<input formControlName="myInputName">
and
this.form = this.fb.group({
myInputName....
});
Upvotes: 74