Implementing dynamic Angular form group array with conditional visibility toggling

Within my angular application, I am faced with the task of implementing a complex form. Users should be able to dynamically add, remove, and modify elements within the form.

Each element consists of a group of inputs, where some are conditionally hidden or visible based on the selection of dropdowns within the group. These conditional groups may have varying quantities of fields, with some being required and others not.

No new array element can be added until all visible fields are validated. Additionally, the entire form needs to be easily serialized and deserialized for backend storage.

I believe that utilizing form arrays is the way to go. I have explored some third-party libraries like ng dynamic forms, but I am still unsure how to achieve what I require. Any suggestions on how I can implement this?

https://i.sstatic.net/HKlEx.png

The form is intended for adding, editing, and removing the following objects:

tasks": [
        {
            "uuid": "4e7e6737-8557-4a05-bb78-808f8a10daa4",
            "created": "2018-12-17T15:19:07.328408+01:00",
            "title": "TITLE (first input)",
            "modified": "2018-12-17T15:19:07.328440+01:00",
            "task_type": "SEND_EMAIL",
            "time_value": 2,
            "time_unit": "DAYS",
            "time_dir": "BEFORE",
            "process": "5f185517-40df-43e8-b677-3cf929b21638",
            "config": {
                "uuid": "b21f4cdf-22ba-42cd-81fa-7e7f1f84e6d1",
                "created": "2018-12-17T15:20:25.350927+01:00",
                "modified": "2018-12-17T15:20:25.350948+01:00",
                "email_title": "Email title",
                "email_from_address": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b6d3ced7dbc6dad3f6d3ced7dbc6dad398d5d9db">[email protected]</a>",
                "email_from_name": "Hello",
                "email_content": "Email body",
            }
        }
    ],

The dropdown options for the task type include:

  • "SEND EMAIL" - displaying fields such as "Email title", "Email from", "Email content" when selected.
  • "CREATE EVENT" - displaying fields like "Event date and time", "Event duration" when selected.

During deserialization of the form, each task's "config" object will contain specific fields based on the selected type.

Answer №1

One way to simplify the process is by using an array of formGroups. Each formGroup should contain all the fields, regardless of the value of the "dropdown".

It may seem that when creating a form like this:

<form [formGroup]="form">

The variable "form" must be a FormGroup, but it can actually be a FormArray.

Therefore, a complete example could look like this, check out stackblitz

<form [formGroup]="form">
    <div *ngFor="let controls of form.controls;let i=index;let last=last" 
            [formGroupName]="i">
        <input formControlName="field1">
        <input formControlName="field2">
        <button type="button" *ngIf="last" 
            [disabled]="!form.controls[i].valid" (click)="addLine()">
            Add
        </button>
    </div>
  </form>
  {{form?.value|json}}

Whenever we have a FormArray, it is useful to have a function that accepts data or null and returns a FormGroup. Similarly, when we have a Form, a function that takes data and returns the form is beneficial.

  export class AppComponent implements OnInit  {
      form:FormArray;  //Note that our Form is directly a FormArray

      constructor(private fb:FormBuilder){}
      ngOnInit()
      {
        this.form=this.createForm(null); //Initially create an empty form
      }

      createForm(data:any[]):FormArray
      {
         //controls is an array of FormGroup
         let controls:FormGroup[]=
            data?data.map(x=>this.createLineArray(x))
                :[this.createLineArray(null)];
         return this.fb.array(controls);
      }

      createLineArray(data:any):FormGroup
      {
          return this.fb.group({
            field1:[data && data.field2?data.field2:null,Validators.required],
            field2:[data && data.field1?data.field1:null,Validators.required]
          })
      }

      //addLine simply pushes a new line in the array
      addLine()
      {
        this.form.push(this.createLineArray(null));
      }

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

Run JavaScript code whenever the table is modified

I have a dynamic table that loads data asynchronously, and I am looking for a way to trigger a function every time the content of the table changes - whether it's new data being added or modifications to existing data. Is there a method to achieve th ...

Modify the MUI time picker to display as a digital clock inside a DateTimePicker widget

I need to update my MUI DateTimePicker component to use the DigitalClock time picker instead of the Analog Clock picker. The current component has two steps, first picking the date from a calendar and then selecting the time. This change is only necessary ...

What is causing the WebMvc ProfileController to be visible and disrupting the functionality of my static website?

I am currently running an Angular application within a Spring Boot application. When accessing routes like https://localhost:8081/sign-in https://localhost:8081/invalid-url (which redirects to PageNotFoundComponent) everything works fine. However, when ...

Steps for confirming a property setting on an interface

I am working with the following interface export interface Command { id: CommandId; disabled: boolean; } My goal is to verify that the 'disabled' property has been changed. Here are my attempts: 1) Creating an object and checking if t ...

Enhance User Experience - Automatically highlight the first element in Prime NG Menu when activated

I have been working on transitioning the focus from the PrimeNG menu to the first element in the list when the menu is toggled. Here is what I've come up with: In my template, I added: <p-menu appendTo="body" #menu [popup]="true&quo ...

From HTML to Mat Table: Transforming tables for Angular

I am currently facing a challenge with my HTML table, as it is being populated row by row from local storage using a for loop. I am seeking assistance in converting this into an Angular Material table. Despite trying various suggestions and codes recommend ...

Troubleshooting: Custom HTML Attribute in Angular 8 Component HTML Not Functioning as Expected

I have a unique situation in my Angular 8 application where I utilize custom HTML attributes and then use jQuery to fetch those attributes for various styling purposes such as setting background images and adjusting the height of div elements. Below is th ...

Problem: Unable to locate the TypeScript declaration file

I am facing an issue with my TypeScript configuration. I have two files in /src/models/: User.ts and User.d.ts. In User.ts, I am building a class and trying to use an interface declaration for an object defined in User.d.ts. However, User.ts is unable to a ...

Changing a d3 event from JavaScript to Typescript in an Angular2 environment

I am a beginner in Typescript and Angular 2. My goal is to create an Angular2 component that incorporates a d3js tool click here. However, I am facing challenges when it comes to converting it to Typescript. For instance, I am unsure if this code rewrite ...

Angular 2 integration for Oauth 2 popup authorization

I am in the process of updating an existing Angular application to utilize Angular 2. One challenge I am facing is opening an OAuth flow in a new pop-up window and then using window.postMessage to send a signal back to the Angular 2 app once the OAuth proc ...

How can I access a service without the need to import its provider module?

My goal is to grasp the concept of multiple NgModules in an angular application and how they interact, specifically focusing on importing a SharedModule for commonly used services into lazily loaded feature modules. Here's the sequence of steps I fol ...

Tab order in Angular Forms can be adjusted

While constructing a two-column form for simplicity, I utilized two separate divs with flexbox. However, the tabbing behavior is not ideal as it moves down the form rather than moving across when using the tab key to navigate between inputs. I am seeking a ...

Having trouble locating the Nativescript-theme-core file for your Nativescript application?

I'm currently working on a basic barcode scanner app and encountering an unusual error once the app is deployed to a Genymotion emulator. It appears to be searching for the core theme in the incorrect location. Any thoughts on why this issue is occurr ...

Unable to connect to the directive even after adding it as an input

In the error message below, it seems that suggestion 1 might be applicable to my situation. My Angular component has a GcUser input, but I have confirmed that it is part of the module (both the component behind the HTML and the user-detail component import ...

Querying Cloud Firestore with User ID

I'm facing an issue with retrieving a subset of data based on the first letter of the name and including the UID of the document. No matter what I try, it just returns empty data. fetchDataByFirstLetter(firstLetter: string) { this.afs.collection(&a ...

Is there a substitute for useState in a Next.js server component?

With my static site at , the only interactive feature being the dark mode toggle, I understand that using useState is not feasible in a server component. export default function RootLayout({ children }: { children: React.ReactNode }) { const [darkMode, ...

Implementing automatic selection for MUI toggle buttons with dynamic data

By default, I needed to set the first toggle button as selected import * as React from "react"; import { Typography, ToggleButton, ToggleButtonGroup } from "@mui/material"; export default function ToggleButtons() { const dat ...

Is it possible for me to exclude generic parameters when they can be inferred from another source?

Imagine having a scenario like this: type RecordsObject<T, K extends keyof T> = { primaryKey: K; data: Array<T>; } where the type K is always derived from the type T. Oftentimes, when I try to declare something as of type RecordsObject, ...

Tips for retrieving data sent through Nextjs Api routing

Here is the API file I have created : import type { NextApiRequest, NextApiResponse } from 'next/types' import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() export default async function handler(req: NextApi ...

Syncfusion Angular TreeGrid Hierarchy connectors and lines for improved data visualization

Is it possible to display guiding lines or connectors that represent the hierarchy of data in a Tree Grid? You can see an example with blue lines in the image below: https://i.sstatic.net/yA8vP.png ...