The beauty of crafting intricate forms with Angular's reactive nested

In my Angular project, I am exploring the concept of nesting multiple reactive forms within different components.

For instance, I have a component called NameDescComponent that includes a form with two inputs - one for name and one for description, along with a submit button.

The goal is to reuse this component across various pages and forms. Here's a snippet of the HTML code for the component:

<form [formGroup]="nameDescForm" (ngSubmit)="customEmit()">
    <div fxLayout="row" fxLayoutGap="10px" fxFlex>
      <mat-form-field>
        <input matInput placeholder="Name" formControlName="name">
      </mat-form-field>
      <mat-form-field fxFlex>
        <input matInput placeholder="Description" formControlName="description">
      </mat-form-field>
    </div>
    <div fxLayout="column" fxLayoutGap="10px">
      <button type="submit" mat-raised-button color="primary">
        {{buttonText}}
      </button>
      <div>
      </div>
    </div>
  </form>

And here's an excerpt from the corresponding TypeScript file:

public nameDescForm: FormGroup;

@Input() public buttonText: string;
@Output() public save: EventEmitter<any> = new EventEmitter<any>();
@Output() public nameDescFormEmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();

constructor(fb: FormBuilder) {
this.nameDescForm = fb.group({
'name': ['', Validators.required],
'description': ['']
});
}

public ngOnInit() {
console.log(this.nameDescForm);
this.nameDescFormEmit.emit(this.nameDescForm);
}

public customEmit() {
this.save.emit();
}

Furthermore, in a separate page where I include the NameDescComponent, I embed it within another form like so:

<form [formGroup]="parentForm" (ngSubmit)="customEmit()">

  <app-name-description (nameDescFormEmit)="getNameDescForm($event)" buttonText="Save" (save)="save()"></app-name-description>

  <input type="test" formControlName="test">

</form>

Currently, I pass the nameDescFrom data from its component to the ParentComponent using Output and EventEmitter. Although this approach works, I find myself having to independently manage both forms' validity during submission.

I'm curious if there might be a more efficient way to handle this, perhaps accessing the nameDescFrom directly within the parent form?

Thank you

Answer №1

If you want to combine your form with nested forms and streamline the validation process for all of them, you can utilize the formbuilder to construct the entire model object structure within the main form component. Next, in the HTML template, include custom elements for the sub-forms (e.g., <nested-form>), which will display the nested forms.

Check out this example: https://stackblitz.com/edit/angular-m5fexe)

Helpful Angular documentation links:

Code :

export class Form1Component  {
  @Input() name: string;

  public dummyForm: FormGroup;

  constructor(
      private _fb: FormBuilder,
  ) {
      this.createForm();
  }

  createForm() {
    this.dummyForm = this._fb.group({
      username: ['username', Validators.required],
      nestedForm: this._fb.group({        
        complement1: ['complement1', Validators.required],
        complement2: ['complement2', Validators.required],
      })
    });
  }

  submit() {
    if (this.dummyForm.valid) {
      console.log('form AND subforms are valid', this.dummyForm.value);
    } else {
      console.warn('form AND/OR subforms are invalid', this.dummyForm.value);
    }
  }
}

Template for the Form1Component :

<form [formGroup]="dummyForm" (ngSubmit)="submit()">    
    <div>
      <label for="username">Root Input</label>
      <input type="text" id="username" formControlName="username"/>
    </div>
    <nested-form [parentForm]="dummyForm"></nested-form>
    <button>Send</button>    
  </form>

Nested form code:

export class NestedFormComponent {
  @Input()
  public parentForm: FormGroup;
}

Nested form template :

<form [formGroup]="parentForm">
    <div formGroupName="nestedForm">
      <div>
        <label for="complement1">Nested input 1</label>
        <input type="text" formControlName="complement1"/>
      </div>
      <div>
        <label for="complement1">Nested input 1</label>
        <input type="text" formControlName="complement2"/>
      </div>
    </div>
  </form>

Answer №2

If you want to enhance your Angular forms, consider using a custom form control to streamline the process.

Essentially, a custom component acts as a connection point between the main form and any nested forms, ensuring smooth communication between the two. By listening for changes in the nested form, the custom component can update its value accordingly. Despite containing values from multiple nested form controls, the parent form will view the custom control as a single entity.

To implement this functionality, the custom component must adhere to the ControlValueAccessor interface provided by Angular. This allows for seamless management of the custom control's state, including aspects like value, validity, touch status, and more. With this approach, the parent form can interact with the custom control just like any other form element.

To learn more about this technique, check out these resources:

https://medium.com/@majdasab/implementing-control-value-accessor-in-angular-1b89f2f84ebf

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

Is it possible for Azure static web apps to utilize Azure App Service as their backend?

Is it possible to have a Azure static web app communicate with an existing Azure app service in order for users to download a file generated by the app service? ...

When saving a canvas as an image, it only results in a blank, transparent image

I have a situation where I am iterating through an array of image elements sourced from a local folder, attaching them to a canvas. However, when attempting to save the canvas as an image, it is not being saved correctly. The resulting image appears to mat ...

The Ajax post function successfully executes on the first attempt, but on subsequent tries it does

My goal is to send data from a forum asynchronously to a PHP page and display the response in a specific ID without refreshing the page. The first time I submit, everything works perfectly. The text is successfully sent to append.php and the new list of i ...

Manipulating variables across various methods in TypeScript

I have a simple code snippet where two variables are defined in the Main method and I need to access them from another method. However, I am encountering an issue with 'variables are not defined', even though I specified them in the declerations ...

Vue Router generates a URL containing %2F when dealing with slug routes

I am facing an issue in my Vue 3 application where I need to create a route for dynamic slugs. Currently, when using RouterLink, the generated URL contains %2F instead of actual slashes which is not the desired result. For example, the current URL looks l ...

Ways to iterate through a JSON formatted array variable using JavaScript

Below is my code snippet, used to plot all the locations fetched from my database onto a Google map. $.ajax({ url:"http://localhost/church_finder/index.php/MapController/search_church", type:'POST', data: ...

What is the maximum number of groupings that can be created from a set of numbers within a

I'm trying to figure out how to handle a specific task, but I'm running into some obstacles. When adding numbers to clusters, a number is considered to belong to a cluster if its distance to at least one existing number in the cluster is within a ...

The Google Docs viewer is displaying an empty screen

I have been utilizing the Google Docs viewer on my website: <div class="embed-r embed-responsive-a"> <iframe class="embed-responsive-it" src="https://docs.google.com/viewer?embedded=true&amp;url=http://LINK.PDF"></iframe> </div& ...

Can WebSocket messages be encoded?

Is there a way to encrypt or obscure the data I transmit via websockets? For example, the message looks like this: var encryptedMsg = { type: "message", data: { message: "Hello world!" } } I require the ability to send this message in ...

json How to retrieve the first index value in jQuery

As part of my Ajax loop, I am successfully generating JSON and iterating through the results. My goal is to extract only the first index value of JSON which is name. In jQuery, I have the following code: PHP $jsonRows[] = array( "name" => ...

Changing dates in JavaScript / TypeScript can result in inaccurate dates being displayed after adding days

Recently, I encountered an issue with a simple code snippet that seems to produce inconsistent results. Take a look at the function below: addDays(date: Date, days: number): Date { console.log('adding ' + days + ' days'); con ...

An error occurs when attempting to access a property that does not exist on type 'never'. Why is this considered an error rather than a warning?

I am experiencing an issue with the following code snippet: let count: number | undefined | null = 10; count = null; let result: string | undefined | null = count?.toFixed(2); console.log(`Result: ${result}`); The error message I received is as follows: ...

Retrieve the rendered component color variables exclusively from the global color variable file

color_variables.css: [data-theme='default'] { --avatar_bg: #000; --avatar_text: #fff; --avatar_border: red; --button_bg: blue; --button_text: #fff; --button_border: darkblue; --modal_widget_bg: orange; --footer_bg: yellow; --foo ...

Manipulating the DOM and adding the required attribute to an input element in Angular does not automatically add the ng-invalid class

My web page has a layout configuration set up with flag-like, read-only, mandatory, and hidden controls. Within the parent component layout.component.ts, an API call is made to retrieve layout configuration information from the Web API. To simplify the a ...

The table is not visible when using Bootstrap Vue

Can anyone help me with displaying a table of flowers? I am having trouble making it show up on my page. Not quite sure how to use my data to get the flowers to display properly. Any guidance or advice would be greatly appreciated. <b-table> ...

Utilizing a function that changes a UNIX timestamp to a month/day/year layout

I have been working with ExpressJS and am facing a challenge in converting the UNIX format date to mm/dd/yyyy format when retrieving the date value. I attempted to create a function for this conversion, but encountered an issue where my methods cannot be c ...

Unable to execute unit tests while utilizing imports that are only for types

Check out this solution I have developed a library that includes both a component and a directive, resulting in an import cycle issue. Component import { Component } from '@angular/core'; @Component({ selector: 'lib-resizable', t ...

Add more JSON entries to the data submission in Express

Purpose: My goal is to ensure that the JSON data I submit is formatted correctly when it arrives in the JSON file, regardless of the number of entries I submit. Challenge: Currently, the data I submit does not append properly in the JSON file. It appear ...

Is there a way to conceal JavaScript code?

I am trying to keep my AJAX code hidden from users who view the source of my PHP page. How can I achieve this? Below is the AJAX code that I currently have: <script type="text/javascript"> function Ajax(){ var xmlHttp; try{ xmlHttp=n ...

Help kids find solutions

Here is the HTML code I am working with: <div class = "touch" onclick="do(this)"> <span>text01</span> <span>text02</span> <span>text03</span> <span>text04</span> <div class = "findMe">a ...