I have a send Button, that contains 2 Api in it.
So, if the input box is empty then send the button is disabled.
Now i want 1 condition to work,
After giving Email-Id and click on save button, it must get disabled after one click.
If i edit again on input box then it must be enabled or it must be in disabled state.
Check this
<button (click)="generateEmailOtp(enterSms,enterEmail)" class="btn pull-right otp" [disabled]="buttonDisabled">Some Button</button>
buttonDisabled: boolean = false; //class variable
generateEmailOtp(enterSms,enterEmail) {
this.buttonDisabled = !this.buttonDisabled
// TO DO
// If any error/valdiation fails, again reset this.buttonDisabled = !this.buttonDisabled
}
If you are uisng reactive forms take a look at this stackblitz.
component.ts
import { Component } from '#angular/core';
import { FormGroup, Validators, FormBuilder } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public form: FormGroup;
public isThePreviousEmail: boolean;
private previousEmail: string;
constructor(private fb: FormBuilder) {
this.isThePreviousEmail = true;
this.buildForm();
this.form.valueChanges.subscribe((value) => {
if (this.previousEmail) {
this.isThePreviousEmail = value !== this.previousEmail;
}
});
}
public onSubmit(): void {
this.previousEmail = this.form.value.email;
this.isThePreviousEmail = false;
}
private buildForm(): void {
this.form = this.fb.group({
email: [null, Validators.compose([Validators.required, Validators.email])]
});
}
}
component.html
<form [formGroup]="form" (submit)="onSubmit()">
Email: <input formControlName="email">
<button type="submit" [disabled]="form.get('email').invalid || !isThePreviousEmail">Submit</button>
</form>.
I'm using reacctive form validation to disable the submit button. And a boolean variable which becomes false when user edit the input field.
[disabled]="form.get('email').invalid || !isThePreviousEmail"
and i'm subscrbing to form value changes like this.
this.form.valueChanges.subscribe((value) => {
if (this.previousEmail) {
this.isThePreviousEmail = value !== this.previousEmail;
}
});
And on the onSubmit method i'm setting the value to the previousEmail and making the isThePreviousEmail to false.
public onSubmit(): void {
this.previousEmail = this.form.value.email;
this.isThePreviousEmail = false;
}
This might help you to take an idea.
You can use HTML only for that :
<button
#submitButton
(click)="generateEmailOtp(enterSms,enterEmail); submitButton.disabled = true;"
class="btn pull-right otp"
*ngIf="isConfirmEmailOtp || isSms">Send OTP</button>
And to enable it again once the input changed :
<input
class="form-control col-lg-12"
type="email"
[(ngModel)]="enterEmail"
name="myEmail"
#myEmail="ngModel"
email
pattern="[a-zA-Z0-9.-_]{1,}#[a-zA-Z.-]{2,}[.]{1}[a-zA-Z]{2,}"
(input)="submitButton.disabled = false"
#emailOTP>
Related
I'm trying to create a material username reactive form control using the approach outlined in this tutorial.
The end result should work in a form like this ( the fs-username-form is the custom reactive form component ):
<mat-card class="UsernameFormTestCard">
<form class="UsernameForm" [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<input
matInput
placeholder="First name"
type="text"
formControlName="firstName"
/>
<mat-hint *ngIf="!username">Example Monica</mat-hint>
<mat-error>Please enter your first name</mat-error>
</mat-form-field>
<fs-username-form formControlName="username"></fs-username-form>
<button mat-button type="submit" [disabled]="!form.valid">Submit</button>
</form>
</mat-card>
If the form is valid then the Submit button enables.
And it works ... sort of ... It enables reactively ... but not consistently. The minimum number of characters is set to 4. And the submit button enables when the length is 4. However if we start removing characters from the username the submit button only disables after the length of the username is 2.
This is the Stackblitz demo showing this.
The username-form.component.ts is implemented like this:
import { Component } from '#angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators,
} from '#angular/forms';
#Component({
selector: 'fs-username-form',
templateUrl: './username-form.component.html',
styleUrls: ['./username-form.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: UsernameFormComponent,
},
{
provide: NG_VALIDATORS,
multi: true,
useExisting: UsernameFormComponent,
},
],
})
export class UsernameFormComponent implements ControlValueAccessor, Validator {
//=============================================
// ControlValueAccessor API Methods
//=============================================
disabled: boolean = false;
// Dummy initialization.
//The implementation is passed in
// with registerOnChange.
onChange = (username: string) => {};
onTouched = () => {};
touched = false;
usernameValue = '';
writeValue(username: any): void {
this.usernameValue = username;
}
//=============================================
// Registration API Methods
//=============================================
registerOnChange(onChange: any): void {
this.onChange = onChange;
}
registerOnTouched(onTouched: any): void {
this.onTouched = onTouched;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
setDisabledState(disabled: boolean) {
this.disabled = disabled;
if (disabled) {
this.usernameControl?.disable();
} else {
this.usernameControl?.enable();
}
}
//=============================================
// Validator API Methods
//=============================================
validate(control: AbstractControl): ValidationErrors | null {
console.log('VALIDATE IS GETTING CALLED');
console.log('Is the form valid: ', this.usernameForm.valid);
if (this.usernameForm.valid) {
return null;
}
let errors: any = {};
errors = this.addControlErrors(errors, 'username');
return errors;
}
addControlErrors(allErrors: any, controlName: string) {
const errors = { ...allErrors };
const controlErrors = this.usernameForm.controls[controlName].errors;
if (controlErrors) {
errors[controlName] = controlErrors;
}
return errors;
}
/**
* Registers a call to onChange to inform
* parent forms of valueChanges events.
*/
constructor() {
this.usernameControl?.valueChanges.subscribe((u) => {
this.onChange(u);
});
}
public usernameForm: FormGroup = new FormGroup({
username: new FormControl('', [
Validators.required,
Validators.minLength(4),
]),
});
get username() {
return this.usernameForm.get('username')?.value;
}
get usernameControl() {
return this.usernameForm.get('username');
}
}
The username-form.component.html template looks like this:
<form class="UsernameForm" [formGroup]="usernameForm">
<mat-form-field>
<input
matInput
placeholder="username"
type="text"
formControlName="username"
/>
<mat-hint *ngIf="!username">Example Monica</mat-hint>
<mat-error>Please enter your username</mat-error>
</mat-form-field>
</form>
As can be seen it implements the ControlValueAccessor and Validator interfaces.
Call on onChange are made when the user types in the username field and, if I understand correctly, this in turn should cause the parent form to call validate on the username control.
Why does the submit button in the demo form not update consistently or why the username-form component does not?
Got it working. Instead of checking whether the form containing the control is valid in the validate method, I switched it to check whether the control itself is valid like this:
//=============================================
// Validator API Methods
//=============================================
validate(control: AbstractControl): ValidationErrors | null {
if (this.usernameControl.valid) {
return null;
}
let errors: any = {};
errors = this.addControlErrors(errors, 'username');
return errors;
}
And now it works. This is a new working demo.
I am new in angular. I have one scenario where I need only one field required from 5 fields in the form, means if the user fills at least one field then form makes valid.
Thanks in advance.
Since you need to check for the validity of whole form only if one of the fields is non empty , You can manually set the validity like below :
if(!this.valid){
this.form.setErrors({ 'invalid': true});
}else{
this.form.setErrors(null);
}
Where this.valid is your condition based on which you can set the validity
You can check the example : https://angular-exmphk.stackblitz.io
You can also check the answer : FormGroup validation in "exclusive or" which does form validation based on some condition
Hope this helps
See Custom Validators and Cross-field validation in https://angular.io/guide/form-validation
Exact example from our app, where at least one phone number field must be entered. This is a Validator Function, i.e. implements https://angular.io/api/forms/ValidatorFn
import { AbstractControl } from "#angular/forms";
import { Member } from "../../members/member";
export function phone(control: AbstractControl): {[key: string]: any} {
if (!control.parent) return;
const form = control.parent;
const member: Member = form.value;
if (member.contactphone || member.mobile || member.contactphonesecond) {
[
form.controls['contactphone'],
form.controls['mobile'],
form.controls['contactphonesecond']
].forEach(control => {
control.setErrors(null);
});
} else return {'noPhone': 'None of contact phones is specified'};
}
Member is our class that defines all the form fields, your code will be different but the example of the custom validator should help.
Check this example of phone number validator
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { NumberValidator } from '../validators/form-validators';
constructor(
private fb: FormBuilder){}
FormName: FormGroup = this.fb.group({
phoneNumber: ['', NumberValidator]
});
in form-validator file
import { AbstractControl, ValidatorFn } from '#angular/forms';
export const NumberValidator = (): ValidatorFn => {
return (control: AbstractControl): { [key: string]: any } | null => {
const mobileRegex = /^[()-\d\s]*$/g;
const result = mobileRegex.test(control.value);
return result ? null : { mobileInvalid: { value: control.value } };
};
};
let me know if you have any doubts.
<form [formGroup]="formdata">
<div class="form-group">
<label for="fieldlabel1">fieldLabel1</label>
<input type="text" id="fieldlabel1" formControlName="fieldName1" class="form-control"><br>
<label for="fieldlabel2">fieldLabel2</label>
<input type="text" id="fieldlabel2" formControlName="fieldName2" class="form-control">
</div>
</form>
<div class="row">
<div class="col-sm-12">
<button type="submit" value="submit" (click)="somefunctioncall()" [disabled]="formdata.invalid">
Submit
</button>
</div>
</div>
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { OnInit } from '#angular/core';
export class test {
formdata: FormGroup;
ngOnInit() {
this.formdata = new FormGroup({
fieldName1: new FormControl("", Validators.compose([
Validators.required
])),
fieldName2: new FormControl("", Validators.compose([
// remove required validation for one you dont need.
]))
})
}
}
My task is to create an account information web page which consists of 4 pre-filled fields (given name, family name, username and email) and a common save button at the bottom. User can change any field by the respective field. I want save button to be enabled only if user changes any fields. Any method to track state changes? My code is as follows:
<mat-card-content>
<div class="form-group">
<mat-form-field class="simple-form-field-50">
<input matInput placeholder="Given name" name="givenName" formControlName="givenName">
</mat-form-field>
<mat-form-field class="simple-form-field-50">
<input matInput placeholder="Family name" name="familyName" formControlName="familyName">
</mat-form-field>
<br>
<mat-form-field>
<input matInput placeholder="Email" name="email" formControlName="email">
</mat-form-field>
<br>
<button
[disabled]="waiting"
class="simple-form-button"
color="primary"
mat-raised-button
type="submit"
value="submit">
Save
</button>
</div>
</mat-card-content>
My Code Output:
Since you're using a Reactive Form, you can use valueChanges on the FormGroup.
Since it is of type Observable, you can subscribe to it to set a variable of type boolean that will be used in the template to enable the button.
...
#Component({...})
export class AppComponent {
form: FormGroup;
disableButton = true;
ngOnInit() {
...
this.form.valueChanges.subscribe(changes => this.disableButton = false);
}
}
And in your template:
<form [formGroup]="form">
...
<button [disabled]="disableButton">Submit</button>
</form>
UPDATE:
If you want to disable it when values don't really change, check for the current value of the form with the previous value:
import { Component } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
form: FormGroup;
disableButton = true;
userValue = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe#domain.com'
}
ngOnInit() {
this.form = new FormGroup({
firstName: new FormControl(),
lastName: new FormControl(),
email: new FormControl()
});
this.form.patchValue(this.userValue);
this.form.valueChanges.subscribe(changes => this.wasFormChanged(changes));
}
private wasFormChanged(currentValue) {
const fields = ['firstName', 'lastName', 'email'];
for(let i = 0; i < fields.length; i++) {
const fieldName = fields[i];
if(this.userValue[fieldName] !== currentValue[fieldName]) {
console.log('Came inside');
this.disableButton = false;
return;
}
}
this.disableButton = true;
}
}
NOTE: StackBlitz is updated accordingly.
Here's a Working Sample StackBlitz for your ref.
onChange(targetValue : string ){
console.log(targetValue );}
<input matInput placeholder="test" name="test" formControlName="testNM" (input)="onChange($event.target.value)">
This is called Dirty Check.
You may find this SO answer very useful:
https://stackoverflow.com/a/50387044/1331040
Here is the guide for Template-Driven Forms
https://angular.io/guide/forms
Here is the guide for Reactive Forms
https://angular.io/guide/reactive-forms
And here is the difference between two concepts
https://blog.angular-university.io/introduction-to-angular-2-forms-template-driven-vs-model-driven/
Hope these help.
I would do something like this:
form: FormGroup;
disableButton = true;
originalObj: any;
ngOnInit() {
this.form = new FormGroup({
control: new FormControl('Value')
});
this.originalObj = this.form.controls['control'].value; // store the original value in one variable
this.form.valueChanges.subscribe(changes => {
if (this.originalObj == changes.control) // this if added for check the same value
{
this.disableButton = true;
}
else {
this.disableButton = false;
}
}
);
}
WORKING EXAMPLE
Using Angular Reactive Forms for validation of Email.
I added Validators Required and Validators Email but Its displaying both as shown in below image. I just want one Error to be displayed at a time.
HTML Code :
<form [formGroup]="NamFomNgs">
<label>Email :
<input type="email" name="MylHtm" formControlName="MylNgs">
</label><br>
<div class="ErrMsgCls" *ngIf="(NamFomNgs.controls['MylNgs'].touched || NamFomNgs.controls['MylNgs'].dirty) &&
!NamFomNgs.controls['MylNgs'].valid">
<span *ngIf="NamFomNgs.controls['MylNgs'].errors.required">This field is required</span>
<span *ngIf="NamFomNgs.controls['MylNgs'].errors.email">Enter valid email</span>
</div><br>
<button [disabled]="!NamFomNgs.valid">Submit</button>
</form>
Typescript Code :
NamFomNgs:FormGroup;
constructor(private NavPkjVaj: ActivatedRoute, private HtpCncMgrVaj: HttpClient,private FomNgsPkjVaj: FormBuilder)
{
this.NamFomNgs = FomNgsPkjVaj.group(
{
MylNgs:[null,Validators.compose([
Validators.required,
Validators.email ])]
});
}
I feel it's a bug in angular form.
You can create common component to display validation message:
custom-validation-with-error-message
HTML:
<div *ngIf="errorMessage !== null">
{{errorMessage}}
</div>
constrol-message.component.ts:
import { OnInit } from '#angular/core';
import { Component, Input } from '#angular/core';
import { FormGroup, FormControl, AbstractControl } from '#angular/forms';
import { CustomValidationService } from '../custom-validation.service'
export class ControlMessageComponent implements OnInit {
ngOnInit() {
}
#Input() control: FormControl;
constructor() { }
/**
* This method is use to return validation errors
*/
get errorMessage() {
for (let propertyName in this.control.errors) {
if (this.control.errors.hasOwnProperty(propertyName) && this.control.touched) {
return CustomValidationService.getValidatorErrorMessage(this.getName(this.control), propertyName, this.control.errors[propertyName]);
}
if (this.control.valueChanges) {
return CustomValidationService.showValidatorErrorMessage(propertyName, this.control.errors[propertyName])
}
}
return null;
}
/**
* This method used to find the control name
* #param control - AbstractControl
*/
private getName(control: AbstractControl): string | null {
let group = <FormGroup>control.parent;
if (!group) {
return null;
}
let name: string;
Object.keys(group.controls).forEach(key => {
let childControl = group.get(key);
if (childControl !== control) {
return;
}
name = key;
});
return name;
}
}
I am trying to disable a form control when one of my form group value changes but the problem is that the method disable() and enable() also updates the form status, so I have an infinite loop.
#Component({
selector: 'my-app',
template: `
<form [formGroup]="questionForm">
<input type="text" formControlName="question" />
<input type="text" formControlName="other"/>
</form>
`,
})
export class App {
constructor(private fb: FormBuilder) {
this.questionForm = this.fb.group({
question: [''],
other: ['']
});
}
ngOnInit() {
this.questionForm.valueChanges.subscribe(v => {
if(v.question === 'test') {
this.questionForm.get('other').disable();
} else {
...enable()
}
})
}
}
How can I solve this problem?
Well, according to the official docs you could use an directive for custom validation, this could of course in your case could be applied with logic of checking what the user has input and then disable and enable the other field. There's quite a lot of code there, sooooo...
...You could also do a smaller hack if you do not want all that bunch of code. Let's call a function that checks what user has typed. We also need to bind this, so we can refer to the questionForm:
this.questionForm = this.fb.group({
question: ['', [this.checkString.bind(this)]],
other: ['']
});
Then the function:
checkString(control: FormControl) {
if(control.value == 'test') {
this.questionForm.get('other').disable()
}
// we need to check that questionForm is not undefined (it will be on first check when component is initialized)
else if (this.questionForm) {
this.questionForm.get('other').enable()
}
}
This seems to serve it's purpose.
Demo
Hope this helps! :)
I would first add a property isDisabled to your component, then I would use the [disabled] directive. This would solve your looping issue.
#Component({
selector: 'my-app',
template: `
<form [formGroup]="questionForm">
<input type="text" formControlName="question" />
<input type="text" formControlName="other" [disabled]="isDisabled" />
</form>
`,
})
export class App {
isDisabled = false;
constructor(private fb: FormBuilder) {
this.questionForm = this.fb.group({
question: [''],
other: ['']
});
}
ngOnInit() {
this.questionForm.valueChanges.subscribe(v => {
if (v.question === 'test') {
this.isDisabled = true;
} else {
this.isDisabled = false;
}
})
}
}
With reactive forms, you do it something like this:
question: {value: '', disabled: true }
Adding another answer to work around the reactive forms issue using two controls and an *ngIf directive. See https://github.com/angular/angular/issues/11324.
This solution may not be ideal as it requires that you add a second form control that will appear when the conditions for changing the isDisabled property are met. This means there are two controls to manage when submitting the form.
#Component({
selector: 'my-app',
template: `
<form [formGroup]="questionForm">
<input type="text" formControlName="question" />
<input *ngIf="isDisabled" type="text" formControlName="other" disabled />
<input *ngIf="!isDisabled" type="text" formControlName="other2" />
</form>
`
providers: [FormBuilder]
})
export class App {
isDisabled = true;
constructor(private fb: FormBuilder) {
this.questionForm = this.fb.group({
question: [''],
other: [''],
other2: ['']
});
}
ngOnInit() {
this.questionForm.valueChanges.subscribe(v => {
if(v.question === 'test') {
this.isDisabled = false;
} else {
this.isDisabled = true;
}
});
}
}
Check out or play with the plunker if you wish:
https://plnkr.co/edit/0NMDBCifFjI2SDX9rwcA