All Course > Angular > Angular Material Jan 03, 2020

Advanced web forms in Angular

Angular forms have become indispensable components in contemporary web applications, serving as vital tools for validating and gathering user inputs. In our day-to-day online interactions, various types of web forms are prevalent, ranging from simple login forms that collect essential information like usernames and passwords. These forms play a pivotal role in enhancing user engagement and interactivity within Angular applications.

Consider an inventory management system, a complex framework that necessitates the collection of numerous user inputs simultaneously. Angular, with its high-level APIs, empowers developers to create and dynamically manage advanced forms within Angular applications. In this tutorial, we embark on crafting an intricate web form utilizing these powerful Angular APIs.

To enhance the user interface of our form, we will leverage various user input fields provided by the Angular Material module. The comprehensive list of widgets includes string input, number input, text area, select list, radio button, and check box. This diversity ensures the flexibility and richness of our form, catering to a wide array of input requirements.

  • String input
  • number input
  • Text area
  • Select list
  • Radio button
  • Check box

If you’re new to Angular application development, you can refer to the starter guide for a quick introduction. Additionally, for a deeper understanding of Angular Material components, the Angular Material guide is a valuable resource.

Before diving into coding, it’s essential to review the project prerequisites. Mismatched versions of Node, NPM, and Angular CLI can lead to compilation or runtime errors, affecting the project’s overall functionality. Taking a moment to ensure compatibility will pave the way for a smoother development process.

Project Prerequisites

Before delving into the development of our advanced web form in Angular, it’s crucial to ensure that your environment is equipped with the necessary prerequisites. We will be utilizing specific versions of Angular CLI, Node, and the NPM package manager, so let’s take a moment to verify and match these versions for optimal compatibility.

  • Angular CLI: 12.0.5
  • Node: 14.15.1
  • Package Manager (NPM): 6.14.8
  • Angular: 12.0.5

Confirming these versions is essential for a seamless development experience. You can cross-reference the package versions listed below to ensure a consistent environment.

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1200.5
@angular-devkit/build-angular   12.0.5
@angular-devkit/core            12.0.5
@angular-devkit/schematics      12.0.5
@angular/cdk                    12.2.13
@angular/material               12.2.13
@schematics/angular             12.0.5
rxjs                            6.6.7
typescript                      4.2.4

Once you’ve confirmed your environment’s readiness, we can proceed to create a new Angular application. The Angular CLI will be our go-to tool for running and managing this application, and we’ll leverage the NPM package manager to handle the required packages . This streamlined approach ensures an efficient and organized development process for our advanced Angular web form.

Create a new Angular application

Embarking on the journey to create our advanced Angular web form begins with setting up a new Angular application. Utilizing the Angular CLI streamlines this process, ensuring a quick and efficient start. To initiate the creation of our application, execute the command ng new angular-advanced-forms in the terminal. This command, detailed in this starter guide, generates the essential file structure needed for a fresh Angular project.

Once the project installation is complete, navigate to the app.component.html file and clear its contents. Now, launch the application using the command ng serve -o, where the -o flag prompts Angular to open the application on the default port (4200) upon readiness. If the setup is successful, you should observe an empty page in your browser. This emptiness is intentional, as we’ve removed the default content from the app.component.html file.

This initial setup lays the foundation for our advanced forms project, and from here, we’ll progressively build upon this structure to create a sophisticated and dynamic Angular web form.

Install required dependencies

To bring our advanced Angular form to life, we need to integrate essential dependencies. Execute the following commands in the terminal to install the required libraries and frameworks for our project:

Angular Material library

npm i @angular/[email protected]

Bootstrap theming framework

ngx-abstract-control-as

ngx-abstract-control-as serves as a helpful library, preventing strict template-checking errors. After executing these commands, your package.json file should reflect the updated dependencies.

Now, let’s ensure Angular recognizes Bootstrap by manually adding the Bootstrap CSS file to the styles array in the angular.json file. Open angular.json and update the styles array as follows:

{
  "styles": [
              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
              "./node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.css"
            ],
}

Keep in mind that changes to the angular.json file won’t take effect until you recompile the project. These dependencies provide the foundation for creating a visually appealing and functional advanced Angular web form.

Overview of the web form

Before we delve into the Angular files, let’s take a moment to understand the web form we’re about to create. For a visual guide, you can check out this quick introduction video to get a sneak peek into the Car Park Managing system we’re developing. This sample web form is designed to collect various parking details, making it a comprehensive tool for managing parking spaces effectively.

Here’s a breakdown of the key fields within the form:

  • Group: Represents different groups in the park and functions as a select list.

  • Parking Category: Offers category options such as Event, Car Parking, and Airport. Choosing the Airport category opens additional input fields, including Nearest Airport, Distance to Airport, and Shuttle Service Availability.

  • Parking Type: Encompasses different parking types, like Covered Valet and Covered Self-Park.

  • Pricing Type: Includes variations such as monthly, hourly, and weekly. Opting for hourly prompts the display of the “Upto Price” section, featuring weekday and overnight subsections. Users can also add custom prices by setting “Is Custom Price” to true, allowing for the addition of multiple “Upto Prices.”

  • Other Price Type: Introduces new input fields for adding details about alternative pricing types.

  • Additional Fees: Opens up input fields for additional fee details.

As demonstrated in this video, the “Upto Price,” “Custom Price,” “Other Pricing Type,” and “Additional Fees” sections function as loops within the form, creating a dynamic and user-friendly experience. This overview provides a glimpse into the intricate details our Angular form will capture for efficient management of parking resources.

Create the advanced web form

Now, let’s dive into creating the forms in Angular. In our project, the default app component and app module are already in place. To maintain simplicity, we’ll leverage the default app component for developing our form. At this juncture, you should have the following files within your new Angular app:

  • app.component.ts
  • app.component.html
  • app.component.css
  • app.module.ts

These files form the foundational structure of our Angular application. The TypeScript file, app.component.ts, will house the logic for our form, while the HTML file, app.component.html, will define the visual elements and structure of the form. The associated CSS file, app.component.css, enables us to style our form, providing a polished appearance. Lastly, the app.module.ts file serves as the module configuration for our Angular app, orchestrating the integration of various components.

With these foundational files in place, we’re poised to begin crafting our Angular form, a pivotal element for collecting and validating user inputs in our web application.

Create Angular Interfaces

The initial phase involves the creation of interfaces tailored for our form. In TypeScript, interfaces play a crucial role in defining custom data types, promoting consistency across the application. To establish these interfaces, let’s generate a new file named data.interface.ts within the app folder. In this file, we’ll craft interfaces for various form components such as Group, ParkingCategory, ParkingType, PricingType, Airport, Dates, Hour, Time, Minute, OtherPricingType, and CheckoutType.

The TypeScript code snippet below showcases the interfaces:

export interface Group {
  value: string;
  viewValue: string;
}

export interface ParkingCategory {
  value: string;
  viewValue: string;
}

export interface ParkingType {
  value: string;
  viewValue: string;
}

export interface PricingType {
  value: string;
  viewValue: string;
}

export interface Airport {
  value: string;
  viewValue: string;
}

export interface Dates {
  value: string;
  viewValue: string;
}

export interface Hour {
  value: string;
  viewValue: string;
}

export interface Time {
  value: string;
  viewValue: string;
}

export interface Minute {
  value: string;
  viewValue: string;
}

export interface OtherPricingType {
  value: string;
  viewValue: string;
}

export interface CheckoutType {
  value: string;
  viewValue: string;
}

These interfaces act as blueprints, defining the structure and characteristics of various form elements, ensuring a standardized data type implementation throughout our Angular application.

Create Angular Service

In Angular, services play a pivotal role as special objects that persist throughout a component’s lifecycle, offering a centralized hub for data management. In our case, we are creating a service to act as the data source for our form. This service, named DataService, will contain dummy data for all the select lists within the Angular form. It’s important to note that in real-world applications, data would typically be sourced from APIs or databases.

To implement this service, a new file called data.service.ts is created in the app folder. The service is designed to provide dummy data for various form elements, such as Group, ParkingCategory, ParkingType, PricingType, Airport, Dates, Hour, Time, Minute, OtherPricingType, and CheckoutType. Below is the TypeScript code snippet showcasing the implementation.

import { Injectable } from '@angular/core';
import { 
  Group, 
  ParkingCategory, 
  ParkingType, 
  PricingType, 
  Airport, 
  Dates, 
  Hour, 
  Time, 
  Minute, 
  OtherPricingType, 
  CheckoutType } from './data.interface';

@Injectable()
export class DataService {

  groups: Group[] = [
    { value: 'group_1', viewValue: 'Group 1' },
    { value: 'group_2', viewValue: 'Group 2' },
    { value: 'group_3', viewValue: 'Group 3' }
  ];

  categories: ParkingCategory[] = [
    { value: 'event', viewValue: 'Event' },
    { value: 'car_parking', viewValue: 'Car Parking' },
    { value: 'airport', viewValue: 'Airport' }
  ];

  parking_types: ParkingType[] = [
    { value: 'outdoor_self_park', viewValue: 'Outdoor Self Park' },
    { value: 'covered_valet', viewValue: 'Covered Valet' },
    { value: 'covered_self_park', viewValue: 'Covered Self Park' },
    { value: 'outdoor_valet', viewValue: 'Outdoor Valet' },
    { value: 'covered_self_park_and_valet', viewValue: 'Covered Self Park & Valet' }
  ];

  pricing_types: PricingType[] = [
    { value: 'daily', viewValue: 'Daily' },
    { value: 'hourly', viewValue: 'Hourly' },
    { value: 'monthly', viewValue: 'Monthly' },
    { value: 'weekly', viewValue: 'Weekly' },
    { value: 'voucher', viewValue: 'Voucher' },
    { value: 'parking_pass', viewValue: 'Parking Pass' }
  ];

  airports: Airport[] = [
    { value: 'airport 1', viewValue: 'Airport 1' },
    { value: 'airport 2', viewValue: 'Airport 2' },
    { value: 'airport 3', viewValue: 'Airport 3' },
  ];

  dates: Dates[] = [
    { value: 'sunday', viewValue: 'Sunday' },
    { value: 'monday', viewValue: 'Monday' },
    ...,
    { value: 'saturday', viewValue: 'Staturday' }
  ];

  hours: Hour[] = [
    { value: '0.5', viewValue: '0.5' },
    { value: '1', viewValue: '1' },
    ...,
    { value: '5', viewValue: '5' },

  ];

  times: Time[] = [
    { value: '12:00am', viewValue: '12:00 AM' },
    { value: '12:15am', viewValue: '12:15 AM' },
    { value: '12:30am', viewValue: '12:30 AM' },
    { value: '12:45am', viewValue: '12:45 AM' },
    { value: '1:00am', viewValue: '1:00 AM' },
    { value: '1:15am', viewValue: '1:15 AM' },
    ...,
    { value: '11:30pm', viewValue: '11:30 PM' },
    { value: '11:45pm', viewValue: '11:45 PM' }
  ];

  minutes: Minute[] = [
    { value: 'min', viewValue: 'min' },
    { value: 'hr', viewValue: 'hr' }
  ]

  otherPricingTypes: OtherPricingType[] = [
    { value: 'EarlyBird', viewValue: 'EarlyBird' },
    { value: 'MidDay', viewValue: 'MidDay' },
    { value: 'Evening', viewValue: 'Evening' },
    { value: 'AllDay', viewValue: 'AllDay' },
    { value: 'Nightly', viewValue: 'Nightly' },
    { value: 'Weekend', viewValue: 'Weekend' },
    { value: 'Special', viewValue: 'Special' }
  ]

  checkoutTypes: CheckoutType[] = [
    { value: 'Before', viewValue: 'EarlyBird' },
    { value: 'Max Hrs', viewValue: 'MidDay' }
  ]

  constructor() { }

  getGroups(): Group[] {
    return this.groups;
  }

  getCatagories():  ParkingCategory[] {
    return this.categories;
  }

  getParkingTypes(): ParkingType[] {
    return this.parking_types;
  }

  getPricingTypes(): PricingType[] {
    return this.pricing_types;
  }

  getAirports(): Airport[] {
    return this.airports;
  }

  getDates(): Dates[] {
    return this.dates;
  }

  getHours(): Hour[] {
    return this.hours;
  }

  getTimes(): Time[] {
    return this.times;
  }

  getMinutes(): Minute[] {
    return this.minutes;
  }

  getOtherPricingTypes(): OtherPricingType[] {
    return this.otherPricingTypes;
  }

  getCheckoutTypes(): CheckoutType[]{
    return this.checkoutTypes;
  }

}

This service not only holds the dummy data but also provides methods such as getGroups(), getCatagories(), etc., to retrieve this data when needed. The service is an integral part of our Angular form, serving as a centralized data hub for various form components.

Error messages

In web forms, robust validation is crucial for ensuring accurate user inputs. To streamline the management of error messages resulting from form validation, we’ll create a dedicated file named error-messages.ts to house all such messages.

It’s important to note that while this tutorial doesn’t cover validation for all input fields, the approach illustrated here provides a foundation for handling error messages systematically across the project.

In the error-messages.ts file, a TypeScript object named errorMessages is defined, acting as a key-value store for various form validation errors. Each key corresponds to a specific input field, and its associated value is the corresponding error message to be displayed.

export const errorMessages: { [key: string]: string } = {
    group: 'Group is a required field',
    parking_category: 'Parking Category is a required field',
    parking_type: 'Parking Type is a required field',
    pricing_type: 'Pricing Type is a required field',
    price: 'Pricing Type is a required field, and the minimum value is 0',
    fee_label: 'Fee Label is a required field',
    fees_type: 'Fees Type is a required field',
    amount: 'Amount Type is a required field',
    airport_name: 'Airport Name is a required field',
    distance_to_airport: 'Distance to Airport is a required field',
    shuttle_description: 'Shuttle Description is a required field',
    start_day: 'Start Day is required',
    end_day: 'End Day is required',
    hour: 'Hour is a required field',
    price_2: 'Minimum value is 0',
    max_hours: 'Max Hours is a required field',
    overtime_price: 'Price is a required field',
    overtime_end: 'Start is a required field',
    overtime_start: 'End is a required field'
};

By centralizing all error messages in this dedicated file, project maintainability is greatly enhanced. Any future updates or modifications to error messages can be efficiently managed within this single file, promoting a clean and organized project structure. This approach aids in reducing redundancy, enhancing code readability, and simplifying error message maintenance throughout the development lifecycle.

Import material modules

To streamline the integration of Angular Material components into our dynamic form, it is considered a best practice to consolidate all the material module imports in a dedicated file. This approach enhances maintainability and organization by providing a centralized location for managing these imports.

In the app folder, create a new file named material.module.ts and include the necessary Angular Material modules. This file serves as a one-stop-shop for handling all material-related imports, contributing to a cleaner and more structured codebase.

import { NgModule } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatRadioModule } from '@angular/material/radio';

@NgModule({
    imports: [
        MatCardModule,
        MatButtonModule,
        MatFormFieldModule,
        MatInputModule,
        MatSelectModule,
        MatCheckboxModule,
        MatRadioModule,
        MatIconModule
    ],
    exports: [
        MatCardModule,
        MatButtonModule,
        MatFormFieldModule,
        MatInputModule,
        MatSelectModule,
        MatCheckboxModule,
        MatRadioModule,
        MatIconModule
    ]
})

export class MaterialModule {}

By adhering to this practice, the material.module.ts file encapsulates the Angular Material module imports, simplifying the process of managing and updating them. This modularization contributes to improved code readability and ease of maintenance, especially as the project evolves with potential additions or modifications to material components.

Update Angular Module

To ensure the seamless integration of Angular Material components and necessary modules, the app module must be updated. The app module, defined in the app.module.ts file, serves as the central hub for importing and configuring various modules and services. Below is the modified content of the app.module.ts file, reflecting the inclusion of pertinent imports.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { MaterialModule } from 'src/app/material.module';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { NgxAbstractControlAsModule } from 'ngx-abstract-control-as';
import { DataService } from './data.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MaterialModule,
    ReactiveFormsModule,
    FormsModule,
    NgxAbstractControlAsModule
  ],
  providers: [
    DataService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

In this updated configuration, notable additions include,

  • MaterialModule: This is the custom module created to house all Angular Material module imports. It consolidates the various Angular Material components used in the project, promoting a modular and organized approach.

  • ReactiveFormsModule and FormsModule: These modules are essential for handling reactive forms in Angular. They enable the utilization of reactive form features and template-driven forms, respectively.

  • NgxAbstractControlAsModule: This module is crucial for preventing strict template-checking errors. It provides enhanced control over abstract controls in Angular forms.

  • DataService: This service, which acts as the data source, is injected into components to provide dummy data for the select lists in the Angular form. It plays a pivotal role in managing and supplying data throughout the application.

By updating the app module in this manner, the Angular application is equipped with the necessary modules and services for a robust and dynamic form-building experience.

Styling Web Form

To enhance the visual appeal and layout of the web form, custom styles are applied by modifying the app.component.css file. The provided CSS rules contribute to a polished and organized appearance of the form components. Here is an overview of the added styles:

.form-fields {
  width: 100%;
}

This rule ensures that the form fields occupy the entire width of their container, promoting a responsive and fluid layout.

.mat-radio-button~.mat-radio-button {
  margin-left: 16px;
}

Adjusting the spacing between radio buttons, this rule adds a margin to create separation, enhancing readability and user interaction.

.list-group-item {
  padding: 0px 16px !important;
  margin-bottom: -1px !important;
  background-color: #fff !important;
  border: 1px solid #ddd !important;
}

For list items within the form, this rule refines their appearance by adjusting padding, margin, background color, and border properties. The result is a clean and well-defined list presentation.

.price-title {
  padding-left: 16px;
  padding-top: 16px;
}

Styling the pricing title, this rule adds padding to the left and top, ensuring a consistent and visually appealing presentation.

.left-head {
  padding: 2px;
  background-color: aliceblue;
  border-radius: 5px;
}

.right-head {
  padding: 2px;
  background-color: aliceblue;
  border-radius: 5px;
}

These rules style the left and right headers of the form, providing subtle padding and a background color for emphasis. The border-radius adds a touch of modern design with rounded corners.

.form-check {
  padding-top: 16px;
  width: 31%;
  margin: auto;
}

For form checks or checkboxes, this rule adds top padding and centers them within their container, contributing to a balanced and visually pleasing arrangement.

By incorporating these styles, the web form not only achieves a cohesive and professional appearance but also ensures a positive user experience with well-defined and visually appealing elements.

Update App Component

In this Angular app, the AppComponent plays a central role in defining the logic and structure behind the HTML markup, especially in the context of form creation. The updated app.component.ts file incorporates key Angular form-related elements such as FormGroup, FormBuilder, FormArray, and FormControl to facilitate the creation of a dynamic and interactive web form. Additionally, the DataService is injected into the AppComponent constructor, ensuring that the component can access necessary data for select lists when initialized.

The use of FormGroup enables the organization of form controls into a structured model, allowing for better management of the form state. FormBuilder simplifies the process of creating instances of FormGroup and FormControl, reducing boilerplate code. FormArray proves essential for building dynamic forms, permitting the addition of multiple FormGroup instances.

Here is a snippet from the updated app.component.ts file:

import { Component } from '@angular/core';
import { 
  UntypedFormBuilder, 
  UntypedFormGroup, 
  UntypedFormArray, 
  Validators, 
  UntypedFormControl } from '@angular/forms';
import { errorMessages } from './error-messages';
import { MatRadioChange } from '@angular/material/radio';
import { 
  ParkingType, 
  Group, 
  ParkingCategory, 
  PricingType, 
  Airport, 
  Hour, 
  Dates, 
  Time, 
  Minute, 
  OtherPricingType, 
  CheckoutType } from './data.interface';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-advanced-forms';


  groups: Group[] = [];
  categories: ParkingCategory[] = [];
  parking_types: ParkingType[] = [];
  pricing_types: PricingType[] = [];
  airports: Airport[] = [];
  dates: Dates[] = [];
  hours: Hour[] = [];
  times: Time[] = [];
  minutes: Minute[] = []
  otherPricingTypes: OtherPricingType[] = []
  checkoutTypes: CheckoutType[] = []

  pricingSetForm!: UntypedFormGroup;
  errors = errorMessages;
  showAdditionalCharge = false;
  showAirport = false;
  showShuttle = false;
  showPricingType = false;
  showCustomPrice = false;

  constructor(
    private formBuilder: UntypedFormBuilder,
    private dataService: DataService
  ) {
    this.groups = this.dataService.getGroups();
    this.categories = this.dataService.getCatagories();
    this.parking_types = this.dataService.getParkingTypes();
    this.pricing_types = this.dataService.getPricingTypes();
    this.airports = this.dataService.getAirports();
    this.dates = this.dataService.getDates();
    this.hours = this.dataService.getHours();
    this.times = this.dataService.getTimes();
    this.minutes = this.dataService.getMinutes();
    this.otherPricingTypes = this.dataService.getOtherPricingTypes();
    this.checkoutTypes = this.dataService.getCheckoutTypes();
  }

  get pricingFormA() { 
    return this.pricingSetForm.get('pricing') as UntypedFormArray; 
  }

  get uptoPricingFormA() { 
    return this.pricingSetForm
    .get('price_group.upto_price_array') as UntypedFormArray; 
  }

  get otherPricingFormA() { 
    return this.pricingSetForm
    .get('price_group.other_pricing_type_array') as UntypedFormArray; 
  }

  ngOnInit(): void {
    this.createSetPricingForm();
  }

  createSetPricingForm() : void {
    this.pricingSetForm = this.formBuilder.group({
      group: ['', [Validators.required]],
      parking_category: ['', [Validators.required]],
      parking_type: ['', [Validators.required]],
      pricing_type: ['', [Validators.required]],
      price: ['', [Validators.required, Validators.min(0)]],
      additional_fee: ['additional_fee_no', ''],
    });
  }

  onRemovePricing(i: number): void {
    this.pricingFormA.removeAt(i);
  }

  removeNewPrice(j: number, k: number): void {
    const control = <UntypedFormArray>this.pricingSetForm.get('price_group')
      .get('upto_price_array')['controls'][j].get('price_array');
    control.removeAt(k)
  }

  addNewPrice(j: number, x: number): void {
    const control = <UntypedFormArray>this.pricingSetForm.get('price_group')
      .get('upto_price_array')['controls'][j].get('price_array');
    if (control.length < 7) {
      control.push(this.initPriceInUptoPrice());
    }
  }

  initPriceInUptoPrice(): UntypedFormGroup {
    return this.formBuilder.group({
      hour: ['', ''],
      price_2: ['', [Validators.min(0)]],
      first_time: ['', ''],
      first_type: ['', ''],
      first_price: ['', [Validators.min(0)]],
      every_time: ['', ''],
      every_type: ['', ''],
      every_price: ['', [Validators.min(0)]],
      daily_maximum: ['', ''],
    });
  }

  onClickPricing(e: any): void {
    this.pricingFormA.push(this.initPricing());
  }

  initPricing(): UntypedFormGroup{
    return this.formBuilder.group({
      fee_label: ['', ''],
      fees_type: ['', ''],
      amount: ['', [Validators.min(0)]],
    })
  }

  radioChangeAddCharge($event: MatRadioChange): void {
    if ($event.value === 'additional_fee_yes') {
      console.log($event.value)
      this.pricingSetForm.addControl('pricing', 
        new UntypedFormArray([this.initPricing(),]))
      this.showAdditionalCharge = true;
    } else {
      console.log($event.value)
      this.showAdditionalCharge = false;
      this.pricingSetForm.removeControl('pricing');
    }
  }

  removeOtherPrice(l: number): void {
    this.otherPricingFormA.removeAt(l);
  }

  onClickOtherPrice(): void {
    this.otherPricingFormA.push(this.initOtherPricing());
  }

  initOtherPricing(): UntypedFormGroup {
    return this.formBuilder.group({
      other_pricing_type: ['', ''],
      weekday_start: ['', ''],
      weekday_end: ['', ''],
      checkin_start: ['', ''],
      checking_end: ['', ''],
      checkout_type: ['', ''],
      checkout_time: ['', ''],
      checkout_price: ['', [Validators.min(0)]]
    })
  }

  removeUptoPrice(j: number): void {
    this.uptoPricingFormA.removeAt(j);
  }

  getPriceInUptoPrice(form: any): any {
    return form.controls.price_array.controls;
  }

  radioChangeCustomPrice($event: MatRadioChange): void {
    if ($event.value === 'custom_price_yes') {
      this.showCustomPrice = true;
    } else {
      this.showCustomPrice = false;
    }
  }

  onClickUptoPrice(e: any): void {
    this.uptoPricingFormA.push(this.initUptoPricing());
  }

  initUptoPricing(): UntypedFormGroup {
    return this.formBuilder.group({
      start_day: ['', ''],
      end_day: ['', ''],
      h24hour: ['', ''],
      price24hour: ['', ''],
      overnight_enabled: ['', ''],
      start_time: ['', ''],
      end_time: ['', ''],
      overtime_price: ['', ''],
      max_hours: ['', ''],
      overtime_start: ['', ''],
      overtime_enable: ['', ''],
      overtime_end: ['', ''],
      price_array: new UntypedFormArray([this.initPriceInUptoPrice(),]),
    });
  }

  radioChangeShuttleService($event: MatRadioChange): void {
    if ($event.value === 'shuttle_yes') {
      this.showShuttle = true;
      this.pricingSetForm.addControl('shuttle_description', new UntypedFormControl('', [Validators.required]));
    } else {
      this.showShuttle = false;
      this.pricingSetForm.removeControl('shuttle_description');
    }
  }

  onChangeAirport(event: any): void {
    if (event.value.value === 'airport') {
      this.pricingSetForm.addControl('airport_name', 
        new UntypedFormControl('', [Validators.required]));
      this.pricingSetForm.addControl('distance_to_airport', 
        new UntypedFormControl('', [Validators.required]));
      this.pricingSetForm.addControl('shuttle_service', 
        new UntypedFormControl('shuttle_no', [Validators.required]));
      this.showAirport = true;
    } else {
      this.showAirport = false;
      this.showShuttle = false;
      this.pricingSetForm.removeControl('shuttle_description');
      this.pricingSetForm.removeControl('airport_name');
      this.pricingSetForm.removeControl('distance_to_airport');
      this.pricingSetForm.removeControl('shuttle_service');
    }
  }

  onChangePricingType(event: any): void {
    if (event.value.value === 'hourly') {
      this.pricingSetForm.addControl('price_group', 
        this.formBuilder.group({
        custom_price: ['custom_price_no', ''],
        upto_price_array: new UntypedFormArray([this.initUptoPricing(),]),
        other_pricing_type_array: new UntypedFormArray([])
      }));
      this.showPricingType = true;
    } else {
      this.showPricingType = false;
      this.pricingSetForm.removeControl('price_group');
    }
  }

  onSubmit(form: any): void {
    console.log(form.value);
  }

}

This code snippet outlines the foundation for building the dynamic form. Further sections within the AppComponent class would define form controls, implement methods for form interactions, and incorporate additional logic as needed for a comprehensive web form implementation. If there are any specific questions or concerns about this code, feel free to ask in the comments.

In Angular, components contain the logic behind HTML markup. To build the form we will use FormGroup, FormBuilder, FormArray, and FormControl given by Angular. FormArray allows us to create dynamic forms by pushing more than one FormGroups into it. Also, we will inject the DataService in AppComponent’s constructor to load select list data when the component is getting initialized.

Now it is time to update the app.component.ts file with the following content. If you have any questions about this content please add your questions in the comments.

Update HTML markup

Updating the HTML markup is a crucial step in building the Angular form, as it defines the visual structure and layout of the user interface. In this project, to maintain simplicity, the focus is on a single component, the app.component. After incorporating the necessary form controls and elements, the updated app.component.html file reflects the foundational structure of the dynamic form.

Below is an excerpt from the modified app.component.html file:

<mat-card>
  <mat-card-title>TechNetzz Advanced Angular Forms</mat-card-title>

  <form novalidate (ngSubmit)="onSubmit(pricingSetForm)" [formGroup]="pricingSetForm" role="form">
    <mat-card-content class="mat-typography">
      <div class="form-content">
        <div class="field-container">
          <div>
            <div class="row">
              <div class="col-md-3">
                <mat-form-field class="form-fields">
                  <mat-label>Group</mat-label>
                  <mat-select formControlName="group" name="group">
                    <mat-option *ngFor="let group of groups" [value]="group">
                      {{group.viewValue}}
                    </mat-option>
                  </mat-select>
                  <mat-error>
                    {{errors.group}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-3">
                <mat-form-field class="form-fields">
                  <mat-label>Parking Category</mat-label>
                  <mat-select formControlName="parking_category" name="category"
                    (selectionChange)="onChangeAirport($event)">
                    <mat-option *ngFor="let category of categories" [value]="category">
                      {{category.viewValue}}
                    </mat-option>
                  </mat-select>
                  <mat-error>
                    {{errors.parking_category}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-2">
                <mat-form-field class="form-fields">
                  <mat-label>Parking Type</mat-label>
                  <mat-select formControlName="parking_type" name="type">
                    <mat-option *ngFor="let type of parking_types" [value]="type">
                      {{type.viewValue}}
                    </mat-option>
                  </mat-select>
                  <mat-error>
                    {{errors.parking_type}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-2">
                <mat-form-field class="form-fields">
                  <mat-label>Pricing Type</mat-label>
                  <mat-select formControlName="pricing_type" name="pricing_type"
                    (selectionChange)="onChangePricingType($event)">
                    <mat-option *ngFor="let pricing_type of pricing_types" [value]="pricing_type">
                      {{pricing_type.viewValue}}
                    </mat-option>
                  </mat-select>
                  <mat-error>
                    {{errors.pricing_type}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-2">
                <mat-form-field class="form-fields">
                  <span matPrefix>$ &nbsp;</span>
                  <input type="number" min="0" formControlName="price" matInput placeholder="Price">
                  <mat-error>
                    {{errors.price}}
                  </mat-error>
                </mat-form-field>
              </div>
            </div>
            <div class="row" *ngIf="showAirport">
              <div class="col-md-3">
                <mat-form-field class="form-fields">
                  <mat-label>Which Airport is near you?</mat-label>
                  <mat-select formControlName="airport_name" name="airport">
                    <mat-option *ngFor="let airport of airports" [value]="airport">
                      {{airport.viewValue}}
                    </mat-option>
                  </mat-select>
                  <mat-error>
                    {{errors.airport_name}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-3">
                <mat-form-field class="form-fields">
                  <input type="number" min="0" formControlName="distance_to_airport" matInput
                    placeholder="Distance to Airport (in Miles)">
                  <mat-error>
                    {{errors.distance_to_airport}}
                  </mat-error>
                </mat-form-field>
              </div>
              <div class="col-md-3">
                <div class="form-array-plus-remove">
                  <p>Shuttle Service is Available</p>
                </div>
              </div>
              <div class="col-md-3">
                <div class="form-array-plus-remove">
                  <mat-radio-group formControlName="shuttle_service" (change)="radioChangeShuttleService($event)"
                    aria-label="Select an option">
                    <mat-radio-button value="shuttle_yes">Yes</mat-radio-button>
                    <mat-radio-button value="shuttle_no">No</mat-radio-button>
                  </mat-radio-group>
                </div>
              </div>
            </div>
            <div class="row" *ngIf="showShuttle">
              <div class="col-md-12">
                <mat-form-field class="form-fields">
                  <textarea type="text" formControlName="shuttle_description" matInput
                    placeholder="Shuttle Description"></textarea>
                  <mat-error>
                    {{errors.shuttle_description}}
                  </mat-error>
                </mat-form-field>
              </div>
            </div>

            <div *ngIf="showPricingType" formGroupName="price_group">

              <div class="upto_price mob-style">
                <div class="row">
                  <div class="col-md-12">
                    <p><b>Upto Price</b></p>
                  </div>
                </div>
                <div class="row">
                  <div class="col-md-6">
                    <button type="button" (click)="onClickUptoPrice($event)" mat-raised-button color="primary">+ Add new
                      Upto Price</button>
                  </div>
                  <div class="col-md-3">
                    <p>Is Custom Price?</p>
                  </div>
                  <div class="col-md-3">
                    <mat-radio-group formControlName="custom_price" (change)="radioChangeCustomPrice($event)"
                      aria-label="Select an option">
                      <mat-radio-button value="custom_price_yes">Yes</mat-radio-button>
                      <mat-radio-button value="custom_price_no">No</mat-radio-button>
                    </mat-radio-group>
                  </div>
                </div>
                <div class="row" *ngIf="uptoPricingFormA.controls.length>0">
                  <div class="col-md-6">
                    <div class="row">
                      <div class="price-title">
                        <p><b>Weekday</b></p>
                      </div>
                    </div>
                  </div>
                  <div class="col-md-6">
                    <div class="row">
                      <div class="col-md-6" style="padding-top: 16px;">
                        <p><b>Overnight</b></p>
                      </div>
                    </div>
                  </div>
                </div>
                <div class="row" *ngIf="uptoPricingFormA.controls.length>0">

                  <div class="col-md-6 left-head text-center" *ngIf="!showCustomPrice">
                    <div class="row">
                      <div class="col-md-2">
                        Start
                      </div>
                      <div class="col-md-2">
                        End
                      </div>
                      <div class="col-md-2">
                        Hours
                      </div>
                      <div class="col-md-2">
                        Price
                      </div>
                      <div class="col-md-3">
                        Action
                      </div>
                    </div>
                  </div>

                  <div class="col-md-6 left-head text-center" *ngIf="showCustomPrice">
                    <div class="row">
                      <div class="col-md-2">
                        Start
                      </div>
                      <div class="col-md-2">
                        End
                      </div>
                      <div class="col-md-4">
                        Time
                      </div>
                      <div class="col-md-2" style="float: right;">
                        Price
                      </div>
                    </div>
                  </div>

                  <div class="col-md-6 right-head text-center">
                    <div class="row">
                      <div class="col-md-2">
                        Enable
                      </div>
                      <div class="col-md-2">
                        Start
                      </div>
                      <div class="col-md-2">
                        End
                      </div>
                      <div class="col-md-2">
                        Price
                      </div>
                      <div class="col-md-2 text-left">
                        Max Hours
                      </div>
                      <div class="col-md-2 text-left">
                        Action
                      </div>
                    </div>
                  </div>

                </div>
                <div *ngFor="let num of uptoPricingFormA.controls; let j = index">
                  <div [formGroup]="num | asFormGroup">
                    <div class="row">
                      <div class="col-md-6">
                        <div class="row">

                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>Start</mat-label>
                              <mat-select formControlName="start_day" name="start_day">
                                <mat-option *ngFor="let date of dates" [value]="date">
                                  {{date.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.start_day}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>End</mat-label>
                              <mat-select formControlName="end_day" name="end_day">
                                <mat-option *ngFor="let date of dates" [value]="date">
                                  {{date.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.end_day}}
                              </mat-error>
                            </mat-form-field>
                          </div>

                          <div class="col-md-8" *ngIf="!showCustomPrice">
                            <div *ngFor="let price of getPriceInUptoPrice(num); let k = index">
                              <div [formGroup]="price">
                                <div class="row">
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <mat-label>Hour</mat-label>
                                      <mat-select formControlName="hour" name="hour">
                                        <mat-option *ngFor="let hour of hours" [value]="hour">
                                          {{hour.viewValue}}
                                        </mat-option>
                                      </mat-select>
                                      <mat-error>
                                        {{errors.hour}}
                                      </mat-error>
                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <span matPrefix>$ &nbsp;</span>
                                      <input type="number" min="0" formControlName="price_2" matInput
                                        placeholder="Price">
                                      <mat-error>
                                        {{errors.price_2}}
                                      </mat-error>
                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-6">
                                    <div class="form-array-plus-remove" style="width: 64%;margin: auto;">
                                      <button (click)="addNewPrice(j, k)" type="button" mat-icon-button>
                                        <mat-icon>add</mat-icon>
                                      </button>
                                      <span class="app-spacer"></span>
                                      <button *ngIf="k != 0" type="button" (click)="removeNewPrice(j, k)"
                                        mat-icon-button>
                                        <mat-icon>remove</mat-icon>
                                      </button>
                                    </div>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>

                          <div class="col-md-8" *ngIf="showCustomPrice">
                            <div *ngFor="let price of getPriceInUptoPrice(num); let k = index">
                              <div [formGroup]="price">
                                <div class="row">
                                  <div class="col-md-3">
                                    <div class="text-center" style="padding: 16px 0 0 0;">
                                      <p>
                                        First
                                      </p>
                                    </div>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <input type="number" formControlName="first_time" matInput placeholder="Eg: 30">

                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <mat-label>Type</mat-label>
                                      <mat-select formControlName="first_type" name="minute">
                                        <mat-option *ngFor="let minute of minutes" [value]="minute.value">
                                          {{minute.viewValue}}
                                        </mat-option>
                                      </mat-select>

                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <span matPrefix>$ &nbsp;</span>
                                      <input type="number" min="0" formControlName="first_price" matInput
                                        placeholder="">
                                      <mat-error>
                                        {{errors.price_2}}
                                      </mat-error>
                                    </mat-form-field>
                                  </div>
                                </div>
                                <div class="row">
                                  <div class="col-md-3">
                                    <div class="text-center" style="padding: 16px 0 0 0;">
                                      <p>
                                        Every added
                                      </p>
                                    </div>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <input type="number" formControlName="every_time" matInput placeholder="Eg: 1">

                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <mat-label>Type</mat-label>
                                      <mat-select formControlName="every_type" name="minute">
                                        <mat-option *ngFor="let minute of minutes" [value]="minute.value">
                                          {{minute.viewValue}}
                                        </mat-option>
                                      </mat-select>

                                    </mat-form-field>
                                  </div>
                                  <div class="col-md-3">
                                    <mat-form-field class="form-fields">
                                      <span matPrefix>$ &nbsp;</span>
                                      <input type="number" min="0" formControlName="every_price" matInput
                                        placeholder="">
                                      <mat-error>
                                        {{errors.price_2}}
                                      </mat-error>
                                    </mat-form-field>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>

                        <div class="row">
                          <div class="col-md-2">

                          </div>
                          <div class="col-md-2">

                          </div>
                          <div class="col-md-8">
                            <div class="row" *ngIf="!showCustomPrice">
                              <div class="col-md-3">
                                <mat-form-field class="form-fields">
                                  <mat-label>24</mat-label>
                                  <mat-select formControlName="h24hour" name="hour">
                                    <mat-option [value]="24">
                                      24
                                    </mat-option>
                                  </mat-select>
                                </mat-form-field>
                              </div>
                              <div class="col-md-3">
                                <mat-form-field class="form-fields">
                                  <span matPrefix>$ &nbsp;</span>
                                  <input type="number" min="0" formControlName="price24hour" matInput
                                    placeholder="Price">
                                  <mat-error>
                                    {{errors.price_2}}
                                  </mat-error>
                                </mat-form-field>
                              </div>
                              <div class="col-md-6">
                              </div>
                            </div>
                            <div class="row" *ngIf="showCustomPrice">
                              <div class="col-md-3">

                              </div>
                              <div class="col-md-3">

                              </div>
                              <div class="col-md-6">
                              </div>
                            </div>
                          </div>
                        </div>


                      </div>

                      <div class="col-md-6">
                        <div class="row">

                          <div class="col-md-2">
                            <div class="form-check">
                              <mat-checkbox formControlName="overtime_enable"></mat-checkbox>
                            </div>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>Start</mat-label>
                              <mat-select formControlName="overtime_start" name="overtime_start">
                                <mat-option *ngFor="let time of times" [value]="time">
                                  {{time.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.overtime_start}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>End</mat-label>
                              <mat-select formControlName="overtime_end" name="overtime_end">
                                <mat-option *ngFor="let time of times" [value]="time">
                                  {{time.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.overtime_end}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <span matPrefix>$ &nbsp;</span>
                              <input type="number" min="0" formControlName="overtime_price" matInput
                                placeholder="Price">
                              <mat-error>
                                {{errors.price_2}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <input type="number" formControlName="max_hours" matInput placeholder="Max Hours">
                              <mat-error>
                                {{errors.max_hours}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <div class="form-array-plus-remove">
                              <button (click)="removeUptoPrice(j)" type="button" mat-icon-button>
                                <mat-icon>remove</mat-icon>
                              </button>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <div class="other_price mob-style">
                <div class="row">
                  <div class="col-md-12">
                    <p><b>Other Pricing Type</b></p>
                  </div>
                </div>
                <div class="row">
                  <div class="col-md-6">
                    <button type="button" (click)="onClickOtherPrice()" mat-raised-button color="primary">+ Add
                      New
                      Pricing Type</button>
                  </div>
                </div>
                <div class="row" *ngIf="otherPricingFormA.controls.length > 0">
                  <div class="col-md-6 left-head text-center">
                    <div class="row">
                      <div class="col-md-3">
                        Pricing Type
                      </div>
                      <div class="col-md-9">
                        Weekdays
                      </div>
                    </div>
                  </div>
                  <div class="col-md-6 right-head text-center">
                    <div class="row">
                      <div class="col-md-4">
                        Check-In
                      </div>
                      <div class="col-md-4">
                        Check-Out
                      </div>
                      <div class="col-md-2">
                        Price
                      </div>
                      <div class="col-md-2">
                        Action
                      </div>
                    </div>
                  </div>
                </div>
                <div *ngFor="let other_pricing of otherPricingFormA.controls; let l = index">
                  <div [formGroup]="other_pricing | asFormGroup">
                    <div class="row">
                      <div class="col-md-6">
                        <div class="row">
                          <div class="col-md-4">
                            <mat-form-field class="form-fields">
                              <mat-label>Pricing Type</mat-label>
                              <mat-select formControlName="other_pricing_type" name="otherPricingType">
                                <mat-option *ngFor="let otherPricingType of otherPricingTypes"
                                  [value]="otherPricingType">
                                  {{otherPricingType.viewValue}}
                                </mat-option>
                              </mat-select>

                            </mat-form-field>
                          </div>
                          <div class="col-md-4">
                            <mat-form-field class="form-fields">
                              <mat-label>Weekday Start</mat-label>
                              <mat-select formControlName="weekday_start" name="weekday_start">
                                <mat-option *ngFor="let date of dates" [value]="date">
                                  {{date.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.end_day}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-4">
                            <mat-form-field class="form-fields">
                              <mat-label>Weekday End</mat-label>
                              <mat-select formControlName="weekday_end" name="weekday_start">
                                <mat-option *ngFor="let date of dates" [value]="date">
                                  {{date.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.end_day}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                        </div>
                      </div>
                      <div class="col-md-6">
                        <div class="row">
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>Start</mat-label>
                              <mat-select formControlName="checkin_start" name="checking_end">
                                <mat-option *ngFor="let time of times" [value]="time">
                                  {{time.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.overtime_start}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>End</mat-label>
                              <mat-select formControlName="checking_end" name="checking_end">
                                <mat-option *ngFor="let time of times" [value]="time">
                                  {{time.viewValue}}
                                </mat-option>
                              </mat-select>
                              <mat-error>
                                {{errors.overtime_end}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>Type</mat-label>
                              <mat-select formControlName="checkout_type" name="checking_end">
                                <mat-option *ngFor="let checkoutType of checkoutTypes" [value]="checkoutType">
                                  {{checkoutType.viewValue}}
                                </mat-option>
                              </mat-select>

                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <mat-label>Time</mat-label>
                              <mat-select formControlName="checkout_time" name="checkout_time">
                                <mat-option *ngFor="let time of times" [value]="time">
                                  {{time.viewValue}}
                                </mat-option>
                              </mat-select>

                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <mat-form-field class="form-fields">
                              <span matPrefix>$ &nbsp;</span>
                              <input type="number" min="0" formControlName="checkout_price" matInput
                                placeholder="Price">
                              <mat-error>
                                {{errors.price_2}}
                              </mat-error>
                            </mat-form-field>
                          </div>
                          <div class="col-md-2">
                            <div class="form-array-plus-remove">
                              <button (click)="removeOtherPrice(l)" type="button" mat-icon-button>
                                <mat-icon>remove</mat-icon>
                              </button>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <div class="row">
              <div class="col-md-6">
                <p>Are there any additional fees your customer should know about?</p>
              </div>
              <div class="col-md-6">
                <mat-radio-group formControlName="additional_fee" (change)="radioChangeAddCharge($event)"
                  aria-label="Select an option">
                  <mat-radio-button value="additional_fee_yes">Yes</mat-radio-button>
                  <mat-radio-button value="additional_fee_no">No</mat-radio-button>
                </mat-radio-group>
              </div>
            </div>
          </div>
          <div *ngIf="showAdditionalCharge">
            <div class="list-group-item">
              <div *ngFor="let pricing of pricingFormA.controls; let i = index" class="list-group list-group-flush">

                <div [formGroup]="pricingFormA.controls[i] | asFormGroup" class="row">
                  <div class="col-md-3">
                    <mat-form-field class="form-fields">
                      <input type="text" formControlName="fee_label" matInput placeholder="Fee Label">
                      <mat-error>
                        {{errors.fee_label}}
                      </mat-error>
                    </mat-form-field>
                  </div>
                  <div class="col-md-3">
                    <mat-form-field class="form-fields">
                      <input type="text" formControlName="fees_type" matInput placeholder="Fee Type">
                      <mat-error>
                        {{errors.fees_type}}
                      </mat-error>
                    </mat-form-field>
                  </div>
                  <div class="col-md-3">
                    <mat-form-field class="form-fields">
                      <span matPrefix>$ &nbsp;</span>
                      <input type="number" min="0" formControlName="amount" matInput placeholder="Amount">
                      <mat-error>
                        {{errors.price_2}}
                      </mat-error>
                    </mat-form-field>
                  </div>
                  <div class="col-md-3">
                    <div class="form-array-plus-remove">
                      <button type="button" mat-icon-button (click)="onClickPricing($event)" color="primary">
                        <mat-icon>add</mat-icon>
                      </button><span class="app-spacer"></span>
                      <button type="button" *ngIf="i != 0" mat-icon-button (click)="onRemovePricing(i)" color="primary">
                        <mat-icon>remove</mat-icon>
                      </button>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </mat-card-content>
    <mat-card-actions>
      <span class="app-spacer"> </span>
      <button class="dialog-btn" mat-raised-button color="primary" type="submit" [disabled]="pricingSetForm.invalid">Set
        Pricing</button>
    </mat-card-actions>
  </form>

</mat-card>

This snippet showcases the initial structure of the form, featuring elements such as the group selection dropdown and the parking category selection dropdown. The form elements are bound to the corresponding form controls defined in the FormGroup. The mat-select elements leverage Angular Material components for enhanced styling and functionality.

It’s important to note that as the form progresses, additional form controls and sections can be integrated into this HTML markup. The form’s complexity and modularity can be extended by introducing Angular components or dividing the app.component into multiple components based on specific functionalities or sections of the form.

Conclusion

Congratulations! You have successfully reached the culmination of this tutorial, and by now, you should be able to visualize the dynamically generated Angular web form in your browser. With this accomplishment, you’ve acquired the essential skills to construct an advanced web form using Angular. This form, equipped with various features and functionalities, is poised to collect and manage parking details efficiently. As you explore the form in your browser, take pride in the journey you’ve undertaken to comprehend and implement the intricacies of Angular components, modules, services, and form controls. This newfound expertise lays the groundwork for your ability to develop sophisticated web applications with Angular. Cheers to your achievement in creating an advanced Angular web form!

Comments

There are no comments yet.

Write a comment

You can use the Markdown syntax to format your comment.

Tags: angular dynamic forms forms material