Tips for integrating automatic form validation in forms with nested components

Imagine a scenario where you have a simple HTML form with two input fields in a component of an Angular 4 application. The first input field is directly implemented, while the second one is within a child component:

<form #personForm="ngForm">
    <input type="text" required name="firstname [(ngModel)]="firstname"/>
    <app-smart-input required [(model)]="lastname"></app-smart-input>
    <button [disabled]="personForm.valid === false">Send</button>
</form>

The child component's code looks like this:

import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";

@Component({
    selector: "app-smart-input",
    templateUrl: "./smart-input.component.html",
    styleUrls: ["./smart-input.component.css"]
})
export class SmartInputComponent {

    ////////////////
    // PROPERTIES //
    ////////////////

    @Input() model: string;
    @Output() modelChange: EventEmitter<string> = new EventEmitter<string>();

    @Input("required") required: boolean = false;

    /////////////
    // METHODS //
    /////////////

    updateChanges() {
        this.modelChange.emit(this.model);
    }

}

The HTML template for the child component is as follows:

<input type="text" [required]="required" [(ngModel)]="model" (ngModelChange)="updateChanges()" />

At this point, updating the models works flawlessly – both firstname and lastname are correctly defined based on user input.

However, the goal now is to disable the button on the form unless both fields are filled out. The required attribute is used in the <input> implementations to ensure that values are not null or undefined.

Unfortunately, the button is currently only disabled if the firstname field is empty or invalid, disregarding the validation status of the lastname. How can this be addressed?

Please note that while Angular 2 creating reactive forms with nested components may provide some insights, the approach here involves using a template-driven form rather than a reactive form. Nonetheless, there may be ways to adapt the concepts.

Answer №1

To integrate your SmartInputComponent into an Angular Form, you must implement the ControlValueAccessor.

This ensures that changes made in your custom component can be synchronized with the parent form. Below is an example of how you can achieve this by using the SmartInputComponent as a reference.

import { Component, OnInit, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'

@Component({
  selector: 'app-smart-input',
  templateUrl: './smart-input.component.html',
  styleUrls: ['./smart-input.component.css'],
  providers:[ { 
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SmartInputComponent),
    multi: true
  }]
})
export class SmartInputComponent implements ControlValueAccessor {

  @Input() model: any;

  onChange = (_: any) => {};
  onTouched = () => {};

  writeValue(obj: any): void {
    if (obj !== undefined) { 
      this.model = obj;
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    //TODO: enabled/disable your input field if you need to
  }

  updateChanges(val) {
     this.onChange(val);
  }

  updateBlur(){
    this.onTouched();
  }
}

Include the following template:

<input type="text" (input)="updateChanges(myTextBox.value)" (blur)="updateBlur()" #myTextBox [value]="model"/>

When using your component in a form, treat it like other standard controls that Angular already provides ControlValueAccessor implementation for.

<form #personForm="ngForm">
  <input type="text" required name="firstname" [(ngModel)]="firstname" name="firstName" />
  <app-smart-input required [(ngModel)]="lastname" name="secondName"></app-smart-input>
  <button [disabled]="personForm.valid === false">Send</button>

  {{personForm.value | json}}

</form>

By following this approach, the personForm will now capture values for both first and second names.

Refer to the Angular documentation on ControlValueAccessor for more information.

Similar questions

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

Is there a way to show uppercase values in Angular's tooltip as capital letters?

Here is my HTML code: <i [class]="item.icon" [pTooltip]="item.item" tooltipPosition="bottom" ></i> The value inside item.item is 'ATTACHMENT' and I am unable to modify it from the ts file. Is there a way ...

Securing React: Best Practices for State Management

Consider the following scenario: const UserProfile: React.FC<RouteComponentProps> = (props) => { const { isAdmin} = useContext(GlobalContext); if (isAdmin()) { return <CriticalFeature/>; } else { return <NonCritic ...

Is it possible to programmatically create a button to toggle the expansion and collapse of the Column Grouping in AG-Grid?

Looking to add a button that can expand/collapse all Grouped Columns programmatically, similar to the existing expand/collapse icon but for all column groups without relying on the pivot panel. Check out the Column Expand/Collapse icon here I searched th ...

Resolve the Angular proxy issue with the backend

Attempting to troubleshoot a similar problem, but struggling to understand why it's not functioning correctly. The API I am working with is located at: localhost:8080/api/acl/authorize Here is the HTTP client code: const AUTH_URI = "/api/acl/&q ...

Can a Firebase function be configured to automatically retrieve data from an external API at regular intervals?

I am currently in the midst of a project that involves utilizing Firebase to store data retrieved from an external API. I'm curious if there's a way to automate this process on a scheduled basis, such as every two days, and then have the data sav ...

Tips for effectively utilizing generics in typescript

Having trouble understanding how to properly utilize generics. Can someone help me use generics in the following scenario: export interface Location { id: number; address: { houseNumber: string; }; } export const getEuropeLocations = async ( ap ...

It is necessary to sign out users when a specific database value is set to false

Whenever a value in the firebase database is false, I need to shut down the app for maintenance purposes. A problem arises when the user is already logged in, as the function does not trigger unless I reload the app. I am looking for a way to trigger the f ...

Getting Started: Switching default browser to Chrome for Angular 8 application launchIn this guide, we will explore

ng start -o Whenever I run the command above, my Angular application automatically opens in the Edge browser. Is there a specific parameter that needs to be used in order to open the application in Google Chrome by default? ...

Loading an HTML file conditionally in an Angular 5 component

Consider a scenario in my project where I have a testDynamic component. @Component({ templateUrl: "./test-dynamic.html", // This file needs to be overriden styleUrls: ['./test-dynamic.css'] }) export class testDynamic { constructor() ...

Encountering issues while attempting to run an npm install command on the package.json file

Having trouble running npm install to set up my Angular project on a Mac. It seems like the issues are due to working with an older project. npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: @angular-devkit/< ...

Occasionally encountering missing modules with Vscode and NestJs

This situation is really frustrating: I started a brand new NestJs project using the @nestjs/cli command. Everything was going smoothly at first. However, after adding a controller with nest generate controller mycontroller and installing types for jasmine ...

"Encountering an error stating 'SyntaxError: Unable to use import statement outside of a module' when attempting to import a module that itself imports another module for side

I'm currently working on writing Jest tests for my TypeScript code. To ensure compatibility with older browsers, I have included some polyfills and other necessary imports for side effects. Here is a snippet of my code (variables changed for anonymit ...

The pagination button in angular bootstrap UI caused the button to shift when clicked

I am currently using angular bootstrap's UI pagination to display results with next and previous buttons. The result sets are displaying correctly, however, I am encountering an issue when clicking on the pagination buttons. Specifically, the buttons ...

Is it necessary for Vue single file components (.vue files) to use `export default` or is it possible to use named exports instead?

export default may no longer be the recommended way to export modules, as discussed in these resources: After changing my Vue components from this: <script lang="ts"> 'use strict'; import {store} from '../../data/store' ...

How to make a node application run as an executable within an NX workspace

The structure of an NX workspace really caught my attention, which led me to start using it for a new CLI project I was working on. I began by setting up a @nrwl/node:application, but I'm currently facing some issues trying to make it executable. I ...

Encountering a CORS problem when an Angular application communicates with a .NET Core API integrated with the Sustainsys.Saml2 library and Azure Active Directory serving as the Identity

Our team is currently working on implementing SAML authentication in a .NET Core API to handle requests coming from an Angular application. We are utilizing the package Sustainsys.Saml2.AspNetCore2 (version 2.9.2) for .NET 6, and we have successfully set u ...

Typescript combineReducers with no overload

There seems to be an issue with my reducers, specifically with the combineReducers function. While it may be something obvious that I am missing, I keep encountering an error. export default combineReducers<ConfigCategoryState>({ tree: treeReducer( ...

ngsw-worker.js is not integrated with Angular Universal

Our implementation of PWA (@angular/service-worker) is functioning properly in prod mode, but we are encountering an issue in dev mode where the file "ngsw-worker.js" is not being generated. Despite adjusting the registration to enable it on all environmen ...

Am I effectively implementing async await in TypeScript?

I'm not quite sure if I'm using the async/await functionality correctly in my TypeScript and Protractor code. Looking at the code snippet below, the spec uses await to call the page object, which itself is an async/await function. The page object ...

Customizing the Switch component individually for each item fetched from an API in React Native

I'm struggling with setting the switch button individually for each item in my API. Despite trying multiple solutions, none of them seem to work for me. const results = [ { Id: "IySO9wUrt8", Name: & ...