Validate certain elements within a form group in a wizard

Within my 2-step wizard, there is a form group in the first step. When the next page button is clicked on the first step, I want to validate the elements in that form group. My questions are:

1 - Would it be more effective to use 2 separate forms in each wizard step, or use one form group throughout all steps?

2 - If I opt for a single form group, how can I verify the form's validity in each step?

Below is the code snippet from my template:

<wizard #wizard navBarLayout="large-empty-symbols">
    <wizard-step [canExit]="moveDirection.bind(this, formGroup.valid)" navigationSymbol="1">
        <form [formGroup]="formGroup">
            <input-form-control
                [required]="true"
                [group]="formGroup"
                label="Name"
                name="name"
                controlId="name"
                helpText="Enter the Server Unit Name"
            >
            </input-form-control>
            <input-form-control
                [required]="true"
                [group]="formGroup"
                label="Label"
                name="label"
                controlId="label"
                helpText="Enter the Server Unit Label"
            >
            </input-form-control>
            <input-form-control
                [required]="false"
                [group]="formGroup"
                label="Description"
                name="description"
                controlId="description"
                helpText="Enter the Server Unit Description"
            >
            </input-form-control>
            <div class="m-portlet__foot m-portlet__foot--fit">
                <div class="m-form__actions m-form__actions">
                    <div class="row justify-content-center">
                        <div class="col-lg-9 ml-lg-auto">
                            <button routerLink="/asset/server-unit" class="btn btn-secondary">
                                Cancel
                            </button>
                            <button class="btn btn-primary" (click)="checkFormValidity()"
                                id="next-step" type="button" nextStep>
                                Next
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </wizard-step>
    <wizard-step [canExit]="moveDirection.bind(this, formGroup.valid)" navigationSymbol="2">
        <form [formGroup]="step2formGroup">
            <switch-form-control
                label="Enable Execution"
                controlName="execution"
                controlId="enable-execution"
                helpText="Set Job Enable Execution state"
            >
            </switch-form-control>
            <div class="m-portlet__foot m-portlet__foot--fit">
                <div class="m-form__actions m-form__actions">
                    <div class="row justify-content-center">
                        <div class="col-lg-9 ml-lg-auto">
                            <button routerLink="/asset/server-unit" class="btn btn-secondary">
                                Cancel
                            </button>
                            <button class="btn btn-primary" id="previous-step" type="button" previousStep>
                                Previous
                            </button>
                            <button class="btn btn-success" (click)="registerItem()" id="submit" type="button" [loadingBtn]="promise">
                                {{isEdit ? "Update" : "Add"}}
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </wizard-step>
</wizard>

Answer №1

When developing my app, I implemented multiple forms for each wizard step, which proved to work effectively. Wanting to share my experience and assist others facing a similar issue, I decided to document my process:

This is the HTML code I used:

<wizard #wizard navBarLayout="large-empty-symbols">
                <wizard-step [canExit]="moveDirection.bind(this, formGroups.step1formGroup.valid)" navigationSymbol="1">
                    <form [formGroup]="formGroups.step1formGroup">
                        <input-form-control
                                [required]="true"
                                [group]="formGroups.step1formGroup"
                                label="Name"
                                name="name"
                                controlId="name"
                                helpText="Enter the Server Unit Name"
                        >
                        </input-form-control>
                        <div class="m-portlet__foot m-portlet__foot--fit">
                            <div class="m-form__actions m-form__actions">
                                <div class="row justify-content-center">
                                    <div class="col-lg-9 ml-lg-auto">
                                        <button routerLink="/asset/server-unit" class="btn btn-secondary">
                                            Cancel
                                        </button>
                                        <button class="btn btn-primary" (click)="checkFormValidity('step1formGroup')" id="next-step" type="button" nextStep>
                                            Next
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                </wizard-step>
                <wizard-step [canExit]="moveDirection.bind(this, formGroups.step2formGroup.valid)" navigationSymbol="2">
                    <form [formGroup]="formGroups.step2formGroup">
                        <switch-form-control
                                label="Enable Execution"
                                controlName="execution"
                                id="enable-execution"
                                helpText="Set Job Enable Execution state"
                        >
                        </switch-form-control>
                        <div class="m-portlet__foot m-portlet__foot--fit">
                            <div class="m-form__actions m-form__actions">
                                <div class="row justify-content-center">
                                    <div class="col-lg-9 ml-lg-auto">
                                        <button routerLink="/asset/server-unit" class="btn btn-secondary">
                                            Cancel
                                        </button>
                                        <button class="btn btn-primary" id="previous-step" type="button" previousStep>
                                            Previous
                                        </button>
                                        <button class="btn btn-success" (click)="registerItem('step2formGroup')" id="submit" type="button" [loadingBtn]="promise">
                                            {{isEdit ? "Update" : "Add"}}
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                </wizard-step>
            </wizard>

This is the component I created:

export class AppComponent  {

  constructor(formBuilder: FormBuilder) {

              let controlsConfig = { step1formGroup: {
                    "name": ['', Validators.required]
                },
                step2formGroup: {
                    "execution": [true]

                }
            }

            for (let item in this.controlsConfig) {
            this.formGroups[item] = this.formBuilder.group(controlsConfig[item]);
        }
  }

  moveDirection = (validityStatus, direction) => {
        if (direction === MovingDirection.Backwards) {
            return true;
        }
        return validityStatus;
    };

    storeData(dataType, data) {
        console.log(dataType);
        if (!this.formGroups.step1formGroup.controls[dataType] && !this.formGroups.step2formGroup.controls[dataType]) {
            return;
        }
        let formGroup: string = '';
        if (this.formGroups.step1formGroup.controls[dataType]) {
            formGroup = 'step1formGroup';
        } else {
            formGroup = 'step2formGroup';
        }
        this.formGroups[formGroup].controls[dataType].setValue(data.value);
    }

      registerItem(index) {

        if (!this.formGroups[index].valid) {
            this.checkFormValidity(index);
            return;
        }
        this.submit().then(() => {
            this.notificationService.setNotifies({
                message: this.isEdit ? 'Device Edited Successfully.' : 'Device Added Successfully.',
                type: 'success'
            });
            this.router.navigate([this.url]);
        }, error => {
            this.notificationService.setNotifies({
                message: this.errorMapperService.mapper(error.errorCode || ''),
                type: 'danger'
            });
        });
    }

    addItem() {
        let formData = Object.assign({});
        for (let formGroup in this.formGroups) {
            formData = Object.assign(formData, this.formGroups[formGroup].value);
        }
        return this.deviceDataService.registerDevice(this.responseMapper(formData, 'isInAdd'), this.assetName);
    }

    updateItem() {
        let formData = Object.assign({});
        for (let formGroup in this.formGroups) {
            formData = Object.assign(formData, this.formGroups[formGroup].value);
        }
        return this.deviceDataService.updateDevice(this.responseMapper(formData, 'isInUpdate'), this.assetName);
    }

    checkFormValidity(index) {
        for (let item in this.formGroups[index].controls) {
            this.formGroups[index].controls[item].markAsTouched();
        }
    }
}

I utilized ng2-archwizard during this process. Hopefully, this detailed explanation proves helpful to others facing similar challenges.

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

Angular and HTML calendar picker

Is there a way to display a date in an HTML input type="date based on a variable stored in my ts file? The variable ScreenDate is defined in my ts file. <input type="date" [(ngModel)]="ScreenDate"> I tried the above code but it did not work as exp ...

Having trouble getting React app to recognize Sass properly

I have been working on developing a React app using TypeScript and the SASS preprocessor. Here is an example of my code: // Button.tsx import React from 'react'; import './Button.scss'; export default class Button extends React.Compone ...

Updating directives is required when there is a modification in the input

I created a custom directive that controls the opacity of an element based on an input value: import { Directive, ElementRef, HostListener, Input, OnInit } from '@angular/core'; import { Observable, Subscription } from 'rxjs/Rx'; @Dir ...

Error: The function webpackMerge.strategy does not exist

I've been in the process of updating an older Angular project to the latest version of Angular. However, I'm encountering a problem when trying to build, and I'm unsure why this is happening. Below is the error message that I've receiv ...

Identifying the End of an HTML Video in Angular 2

Seeking assistance with detecting the end of an HTML video in Ionic2 (Angular2 and Typescript). The relevant code snippets can be found below: Template: <video poster="" id="v" playsinline autoplay webkit-playsinline onended="vidEnded()"> <s ...

Defining a TypeScript interface specifically tailored for an object containing arrow functions

I encountered an issue while trying to define an interface for the structure outlined below: interface JSONRecord { [propName: string]: any; } type ReturnType = (id: string|number, field: string, record: JSONRecord) => string export const formatDicti ...

Guide to running asynchronous code synchronously in Angular 5

Currently, I have a function that loops through a list of files and for each file, it triggers an async method to manipulate the image and add it to an array. The problem is that the async calls are occurring simultaneously, causing the UI to freeze. My g ...

Angular - a simple method to determine the number of non-empty inputs in a complex template-driven form

As I work on multiple substantial Angular 11 template forms containing basic inputs like text, radiolists, and checkboxes, I am looking for the most effective method to calculate the percentage of completed inputs while the user is actively engaging with ...

The Error message "Property 'data' is not present in Type <void> | AxiosHttpResponse<any>" is indicating that the data property is missing on

When I fetch data for a specific user, I have a promise that I use to setState. Below is the implementation: getUserUsername = (): string => { const { match } = this.props; return match.params.username; }; onFetchUser = () => getUse ...

An effective approach to automatically close an Expansion Panel in an Angular Mat when another one is opened

I am attempting to implement functionality where one expansion panel closes when another is opened. By default, the multi attribute is set to "false" and it works perfectly when using multiple expansion panels within an accordion. However, in this case, I ...

Adding types to computed properties in Vue 3's Composition API is a seamless process

Having an issue where I am trying to add type to computed but keep encountering this error: Overload 1 of 2, '(getter: ComputedGetter<AuthFormType>, debugOptions?: DebuggerOptions | undefined): ComputedRef<AuthFormType>', gave the fol ...

How can I retrieve header values in the canActivate function in Angular?

Depending on the value of userRole received from the header, I need to redirect to different user pages. angular.routing.ts { path: '', pathMatch: 'full', redirectTo: '/login' }, { path: 'user', loadChildren: &apo ...

Testing a NestJS service with multiple constructor parameters can be done by utilizing various techniques such as dependency

Content When testing a service that needs one parameter in the constructor, it's essential to initialize the service as a provider using an object instead of directly passing the service through: auth.service.ts (example) @Injectable() export class ...

Can Angular dynamically obtain the current system time without relying on a backend service?

Is there a way to dynamically display the current system time on a webpage using Angular without having to manually refresh the page? I understand that utilizing Date() can provide the current time, but it may not necessarily be synced with the system tim ...

The test failed to execute due to disconnection (0 occurrences) as no message was received within the 30000 ms timeframe

Currently, I am facing an issue with my Angular application. When I execute the "ng test" command, it displays an error message stating 'Disconnected (0 times), because no message in 30000 ms.' I have tried updating both karma and jasmine package ...

Issue encountered with Ionic and Angular 2: Denied application of style from 'http://localhost:8100/build/main.css' due to unsupported MIME type ('text/html')

Initially, everything was going smoothly with my Ionic build, but things took a turn when I tried to test it on my iPhone. After stopping the server and running ionic serve --address localhost, I noticed that my stylesheet wasn't loading. Even after r ...

Tips for enabling the TypeScript compiler to locate bokeh's "*.d.ts" files

I recently made the switch from Bokeh's convenient inline extension framework to their npm based out of line build system. I'm currently working on getting my extension to build, but I've noticed that Bokeh organizes all TypeScript *.ts.d fi ...

Prevent HTTP method server errors from displaying in the Angular 8 browser console

Currently utilizing Angular 8 and seeking a way to suppress any HTTP errors displaying in the browser console. Here is an example of the error message: zone-evergreen.js:2828 POST http://localhost:3000/api/auth/login 401 (Unauthorized) Within my Error-In ...

Angular - Utilizing FormArrayName within child components

There are three main components involved: consent consent-scope-list consent-scope-item consent includes a FormGroup that houses two FormArrays. consentForm = this.fb.group({ identityScopes: this.fb.array([]), resourceScopes: this.fb.array([]) }); ...

Enhancing MUI themes by incorporating module augmentation for multiple typings

I am looking to create a repository with two directories, each using MUI and TypeScript. Both directories will have their own theme defined in one ThemeProvider per root. In the main index.tsx file in the root directory, I want to specify which component t ...