Angular 6 form validation for input fields

I have 3 input fields that are required, but I want to implement a logic where if one of them is filled, the other two should not be required anymore. I have managed to get this working, but the issue arises when I fill out the form and then remove the value. The form still remains valid.

For example, if I type the Uname: abc, the form is valid. But if I remove the Uname, the form should be invalid.

If I fill out the second input with a username: abcdef, the form is valid.

However, if I remove the username, the form is still showing as valid.

Here is the link to my StackBlitz for reference:

https://stackblitz.com/edit/angular-5l9kb4

Answer №1

Use the provided code snippet below:

validateInput() {
this.myForm.get("name").valueChanges.subscribe(value => {
  if (value  && value.length > 0) {
    this.myForm.controls["name"].clearValidators();
    this.myForm.controls["name"].updateValueAndValidity();
    this.myForm.controls["description"].clearValidators();
    this.myForm.controls["description"].updateValueAndValidity();
  }
});
this.myForm.get("description").valueChanges.subscribe(value => {
  if (value && value.length > 0) {
    this.myForm.controls["name"].clearValidators();
    this.myForm.controls["name"].updateValueAndValidity();
    this.myForm.controls["Uname"].clearValidators();
    this.myForm.controls["Uname"].updateValueAndValidity();
  }
});
this.myForm.get("Uname").valueChanges.subscribe(value => {
  if (value  && value.length > 0) {
    this.myForm.controls["description"].clearValidators();
    this.myForm.controls["description"].updateValueAndValidity();
    this.myForm.controls["name"].clearValidators();
    this.myForm.controls["name"].updateValueAndValidity();
  }
});

Here is a working example on Stackblitz. Feel free to add more validations as needed. I hope this helps resolve your issue.

Answer №2

Make sure to review the

onTypeChange($event.target.value)
function

<form [formGroup]="myForm" (ngSubmit)="submit(myForm.value)">

 <mat-form-field class="full-width">
   value 1
      <input matInput type="text" formControlName="Uname" (input)="onTypeChange($event.target.value)">
    </mat-form-field>

    <mat-form-field class="full-width">value 2
      <input matInput type="text" formControlName="username" (input)="onTypeChange($event.target.value)">
    </mat-form-field>

    <mat-form-field class="full-width" >
      value 3
      <input matInput type="text" formControlName="description"  (input)="onTypeChange($event.target.value)">
    </mat-form-field>
  <button mat-raised-button type="submit" [disabled]="!myForm.valid">Submit</button>
</form>

The TypeScript file

 onTypeChange(searchValue : string ) 
     {     
      console.log("typed value"+searchValue);}
      }

 submit(myForm)
  {
        console.log(myForm);
  }

Answer №3

Although I haven't personally utilized reactive form validation, I believe I have met your requirements. Please review the code snippet below: Fiddle Link : disable form button on condition

import { Component,OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray,
 } from "@angular/forms";
import { FormControl } from '@angular/forms';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  name = 'Angular';
  myForm:FormGroup;
  Uname:any;
  username:any;
  description:string;
  constructor(private fb:FormBuilder) {
  }
  ngOnInit() {
  }

  checkForm() {
    console.log(typeof this.Uname,this.username,this.description)
    if((typeof this.Uname !== 'undefined' && this.Uname != '') || (typeof this.username !== 'undefined' && this.username != '') || (typeof this.description !== 'undefined' && this.description != ''))
      return false;
    else 
      return true;
  }
}

<form name="Mainform" id="Mainform"  (ngSubmit)="submit(myForm.value)">

 <mat-form-field class="full-width">
      <input matInput type="text" 
      name="Uname"
      [(ngModel)]="Uname">
    </mat-form-field>

    <mat-form-field class="full-width">
      <input matInput type="text" 
      [(ngModel)]="username" name="username" >
    </mat-form-field>

    <mat-form-field class="full-width">
      <input matInput type="text" 
      [(ngModel)]="description" name="description">
    </mat-form-field>
  <button mat-raised-button type="submit" [disabled]="checkForm()">Submit</button>
</form>

Answer №4

Your code currently removes the validators when a value is present, but does not reapply them when the value is removed. It is important to re-add the validators in this scenario.

An effective solution is to implement a custom validator for the entire form group. This validator will have visibility into all controls within the group, eliminating the need for individual control subscriptions.

this.myForm = fb.group({
  Uname : [''],
  username : [''],
  description : ['']

},{ validator: formValidator });

function formValidator(formGroup: FormGroup) {
  const uName = formGroup.controls['Uname'].value;
  const username = formGroup.controls['username'].value;
  const description = formGroup.controls['description'].value;
  return uName === '' && username === '' && description === '' ? { 
  oneFieldRequired: {valid: false} } : null;
} 

Answer №5

The responsibility of validating the entire FormGroup ideally falls on a Custom Validator that you can create. This validator will ensure that all fields within the FormGroup are validated as a whole:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray } from "@angular/forms";
import { FormControl } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  myForm: FormGroup;

  constructor(private readonly fb: FormBuilder) { }

  ngOnInit() {
    this.myForm = this.fb.group({
      uName: [''],
      username: [''],
      description: ['']
    }, { validators: anyOneFieldRequired });
  }

  submit(value) {
    console.log('Form Value: ', value);
  }
}

function anyOneFieldRequired(formGroup: FormGroup) {
  const uName = formGroup.controls['uName'].value;
  const username = formGroup.controls['username'].value;
  const description = formGroup.controls['description'].value;
  return uName === '' && username === '' && description === '' ? { oneFieldRequired: true } : null;
}

In your template:

<form [formGroup]="myForm" (ngSubmit)="submit(myForm.value)">
    <mat-form-field class="full-width">
        <input matInput type="text" formControlName="uName">
  </mat-form-field>

  <mat-form-field class="full-width">
    <input matInput type="text" formControlName="username" >
  </mat-form-field>

  <mat-form-field class="full-width">
    <input matInput type="text" formControlName="description">
  </mat-form-field>

  <button mat-raised-button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

Check out this Sample StackBlitz for reference.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring Composite Types with TypeScript's `infer` Keyword

After implementing redux in my project, I found myself including the following code snippet in my store: type CombinedParamTypes<T extends { [key: string]: (state: any, action: any) => any; }> = T extends { [key: string]: (state: infer R, ...

How to retrieve video duration in Angular 2 before uploading the file

Is there a way to retrieve the video duration before uploading it in Angular2? I am looking for a solution without using jQuery. Here is the code snippet I have so far: <input type="file" class="form-control" ng2FileSelect [uploader]="uploader" accept= ...

Deactivate Search Functionality for Users who are not Logged in on an Angular 2 Application

The login component and view are functioning as intended, preventing users from accessing AuthGuard protected routes if they're not logged in. However, I'm facing a challenge with the search bar displayed on the home login screen (actually presen ...

What could be causing the app.component.html expression to be invoked repeatedly in my application?

As I was analyzing the evaluation of an expression in the main template app.component.html, I noticed that the trace appears exactly 5 times every time I refresh the page or click on any link. Curiously, when I placed a trace in the ngOnInit of app.compone ...

Adding a static global constant in webpack dynamically

I'm facing a challenge with adding a global constant to my project using webpack.DefinePlugin. I've successfully added one in the module.exports, but I struggle to do this conditionally. When I declare and use '__VERSION__' in my module ...

What is the best way to include a background image in an Angular selector?

I'm having trouble setting a background image for an Angular selector. Here's the code I'm using: <app-highway-card [ngStyle]="{'background-image':'url(https://cdn.pixabay.com/photo/2021/09/04/09/32/road-6597404__340.j ...

Pinia is having trouble importing the named export 'computed' from a non-ECMAScript module. Only the default export is accessible in this case

Having trouble using Pinia in Nuxt 2.15.8 and Vue 2.7.10 with Typescript. I've tried numerous methods and installed various dependencies, but nothing seems to work. After exhausting all options, I even had to restart my main folders on GitHub. The dep ...

Chai is unable to differentiate between different class types

When using Chai to compare if a returned value of type SimpleModel matches with the expected type SimpleModel, I encountered an error even though my IDE indicated that the types are correct: AssertionError: expected {} to be a simplemodel The setup for t ...

React-Redux: Unable to access the 'closed' property as it is undefined

Encountered a problem when using dispatch() in React-Redux. Specifically, the action below: export const fetchMetrics = () => { dispatch(fetchMetricsBegin); APIService.get('/dashboard/info/') .then((response) => { ...

The 'Promise<void>' type cannot be assigned to the 'Promise<xxx>' type

Attempting the following transaction in TypeScript resulted in a compile error. The error message stated: Type 'Promise<void>' is not assignable to type 'Promise<transactionArgument>'. However, the function returns a value o ...

How to add icons to HTML select options using Angular

Looking for a way to enhance my component that displays a list of accounts with not only the account number, but also the currency represented by an icon or image of a country flag. Here is my current component setup: <select name="drpAccounts" class= ...

Deploy your Angular website for absolutely no cost on Firebase by leveraging the Command Line Interface

Looking to deploy an Angular application on Firebase cloud for free using the CLI? Recently started delving into Angular 9 and exploring Firebase's cloud server capabilities, I'm interested in seeing how simple it is to deploy an app on Firebase. ...

Using Angular2 to Toggle Checkbox Field Enabled and Disabled

As I dynamically render checkbox and combobox fields, the following functionality is performed: If the checkbox field appears as selected based on the API response, then the combo box appears as enabled; otherwise, it would be disabled. If a user selects ...

Displaying grouped arrays efficiently in Angular

I have received data from an API in the form of an array with objects structured like so: [ {"desc":"a", "menu": 1},{"desc":"b", "menu": 2},{"desc":"c", "menu": 1}, ...

Angular 2: Unable to locate collection

Just starting out with Angular2 and attempting to create a Todo app. Here is the file structure I am working with: https://i.sstatic.net/ppYWd.png Review of the todo.service.ts code (located in the shared folder) import { Injectable } from '@angul ...

Swap out the traditional for loop with a LINQ query utilizing the any method

In my TypeScript code, I have the following snippet: public executeTest(test: Test): void { const testFilters: Record<string> = getTestFilters(); let isTestingRequired: boolean = false; for (let i: number = 0; i < testFilters.leng ...

Why don't my absolute paths work on nested folders in React and Typescript?

Struggling to configure absolute paths in my React project, encountering challenges with nested folders and the use of @ prefix. Here's what I have set up in my tsconfig.json: { "compilerOptions":{ "baseUrl":"src", ...

Testing the Child Component's EventEmitter Functionality

In my child component, there is an event emitter that sends a boolean value when the style changes in the parent component: export class ExampleComponent implements OnInit, OnChanges { @Output() statusEvent = new EventEmitter<boolean>(); getS ...

I'm uncertain about the appropriate RabbitMQ subscription endpoint with STOMP

I'm using spring-boot-starter-amqp library to send messages to RabbitMQ. I've configured the exchange as spring-boot-exchange and set the routing key as group.n (where n is variable). I'm trying to retrieve these messages in Angular using n ...

Leverage RxJs Pipe for transforming Observables into various data types

Currently, I am dealing with an Observable<Recipe[]> that I need to transform into an array of a different class called ChartData[]. This transformed array will be used as a data source for creating highcharts graphs, such as column charts and pie ch ...