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 form. This would involve determining the number of non-empty inputs versus the total number of inputs in the form. I currently have ngModel binding on all fields but am unsure how to create a custom validation process for this specific task.

Answer №1

Handling Input Counts with Directive and Service for State Management

  • A directive is created to detect changes in the element and trigger updates.
  • We use a service to store the state information of our form.

Important points: Please take note of the following details:

  • The Service is currently set at the root level, limiting it to work for only one form. To handle multiple forms, you will need to provide the service at a component level or extend the state logic to support multiple forms using keys or similar methods.
  • You may consider replacing the service with more advanced state management systems. The decision to use a service was based on its simplicity.

Note: This solution recalculates the total percentage every time any input is changed for simplicity. For larger forms, optimizing by checking if an input has been counted already may be more efficient.

The Directive

This directive assumes that it will only be used with HTMLInputElement types.

@Directive({selector: '[countInput]'})
export class CountInputDirective implements AfterViewInit, OnDestroy {

  private subscription = new Subscription();

  constructor(private host: ElementRef<HTMLInputElement>, private countService: CountingService) {
  }

  ngAfterViewInit() {
    this.countService.addInput(this.host.nativeElement);
    this.subscription.add(
      fromEvent(this.host.nativeElement, 'keydown')
        .pipe(debounceTime(350))
        .subscribe(change => {
          this.countService.inputStateChanged()
        })
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
    this.countService.removeInput(this.host.nativeElement);
  }
}

You can adjust the event binding according to your specific requirements if not all inputs support key events.

The Service

@Injectable({providedIn: 'root'})
export class CountingService {
  private _inputs: HTMLInputElement[] = [];
  private formUpdateEvent: EventEmitter<void> = new EventEmitter();
  private _filled: number = 0;

  get total() {
    return this._inputs.length;
  }

  get percentageDone() {
    return Math.round(this._filled / Math.max(this.total, 1) * 100);
  }

  get formUpdated() {
    return this.formUpdateEvent.asObservable();
  }

  addInput(element: HTMLInputElement) {
    this._inputs.push(element);
  }

  removeInput(element: HTMLInputElement) {
    const index = this._inputs.indexOf(element);
    if (index > -1) {
      this._inputs.splice(index, 1);
    }
  }

  inputStateChanged() {
    this.recalculateState();
    this.formUpdateEvent.emit();
  }

  private recalculateState() {
    this._filled = 0;
    this._inputs.forEach(element => {
      if (Boolean(element.value))
        this._filled++;
    });
  }
  
}

Implementing the Solution

If you need to perform actions when a change occurs, subscribe to the provided EventEmitter. If you simply want to display the total percentage, access the getter directly.

@Component()
 // inject the service in the component with your form.
 constructor(private count: CountingService) {}
<!-- apply the directive to all inputs requiring counting -->
<form action="">
  <input type="text" countInput>
  <input type="text" countInput>
  <input type="text" countInput>
</form>
{{count.percentageDone}}
</form>

Answer №2

When utilizing template-driven forms, adding custom validation directly to the form is not straightforward. However, there is a workaround to achieve this. By implementing an input change event for the specific control requiring custom validation logic, we can handle it separately. To monitor the progress of a large form, one approach is to break it down into smaller form groups and manage events like on input change specifically for each group. This strategy helps in avoiding the need to iterate through a vast collection of form controls by applying the input change event to the form itself.

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

reasons for utilizing `this.initialState = this.state;`

Could someone help me understand why I am using this.initialState in a React JS class component setup? class Registration extends Component { constructor(props) { super(props); this.state = { username: '', email: '&apo ...

Running a function using a component in Angular 5

I am looking to develop an "action" component that functions similar to an "a" element. However, I need a way to track when the action is complete after clicking on the component. The main objective is to show a message while the action is in progress. He ...

Managing OAuth2 redirections on the frontend: Best practices

I am currently working on implementing an OAuth2 flow for a Single Page Webapp, but I am facing challenges in dealing with Frontend/JavaScript redirects. Regarding the backend setup, I have it all sorted out: utilizing a library that takes care of everyth ...

How can I implement a single-column search feature in a GridView using Javascript in ASP.NET?

I found a Google function for client-side searching in a grid using a textbox Here is the function: <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script> function searchFunction(phrase, ...

Manipulate MySQL data in Node.js by storing it in a variable

Struggling to grasp the concepts of nodeJS/typescript and how to effectively save database query results into variables for return. Seeking assistance to solve the problem faced: Here is a method snippet that needs help: public getAllProducts(): ProductA ...

Selenium in Perl: Facing a puzzling JavaScript error while attempting to resize window

Utilizing the Perl Selenium package known as WWW::Selenium, I have encountered a perplexing JavaScript error while attempting to resize the browser window. The error message reads: "Threw an exception: missing ; before statement". Below is the code snippe ...

Steps for dynamically expanding a textarea in Selenium WebDriver when the element path is constantly changing

I am facing a challenge with resizing a textarea that has a dynamic xpath. I am unable to use the following JavascriptExecutor commands: (JavascriptExecutor) driver.executeScript("document.getElementById('someID').setAttribute('rows', ...

Tips for persisting form values even after refreshing the page - a guide to setting form values that stay in place

When I submit a long form, an external JavaScript validation is triggered to check the input field validity. If all fields pass validation, a jQuery modal appears prompting the user to either register or log in. If the user chooses to register and complet ...

Is it possible to update the variable value in one controller from another controller after an http.get request has been made

I have encountered an issue with updating a variable from one controller to another using a service. Despite my efforts, the variable is not being updated. The goal is to have the variable $scope.names in controller 'select' update in controller ...

Exploring the power of Javascript for number lookup

I am currently working on a coding project using TypeScript and JavaScript to locate a specific number provided by the user within a list. The goal is to display whether or not the number is present in the list when the 'search' button is pressed ...

The response type for HTTP Get in Typescript is specified as "text"

I'm currently working on a Typescript HTTP Get request and need to customize the response type as text. Here's my code snippet: getMessageContent(messageContentId?: string): Observable<string> { const url = this.commonService.getApi ...

Generating a unique event triggered by element class change

Is it possible to create custom JavaScript events that are triggered when elements receive a specific class? I am trying to monitor all elements within a table and perform certain actions once a particular class is added to them. Can this be done, and if ...

Utilize an exported es6 module of a web component within an Angular project

I have developed a library consisting of web components which are exported using rollup. The bundle includes exports like: export { Input, Button }; where Input and Button are ES6 classes defined within the bundle itself. After publishing this library ...

The AJAX callback is unable to access the method of the jQuery plugin

I have a scenario where I am fetching data from an AJAX response and attempting to update a jQuery plugin with that value within the success callback: $.ajax({ url: '/some/url', type: 'GET', dataType: 'json', succ ...

The printing feature in javascript does not include the ability to print values from textboxes

I'm encountering an issue when trying to print an HTML table with textboxes that should get their values from another function. However, the preview is not showing anything. Below is the complete code: <html> <head></head> < ...

Populate Vue 3 Element-plus Virtualized Table with actual data

As a newcomer to frontend development, I am currently working on integrating Element-plus Virtualized Table with actual data. Here is the basic structure of the example: const generateColumns = (length = 10, prefix = 'column-', props?: any) => ...

What is the best way to retrieve a JSP parameter dynamically or how can one create a JSP parameter on the

Currently learning JSP and ajax simultaneously. In the process of creating a dynamic tab that can be added or removed using these steps. Looking to pass parameters from controller to the content area of the newly added tab. 1. When clicking on the &apos ...

modifying the appearance of the play button through a JavaScript event without directly altering it

I am currently working on building a music player from scratch using HTML, CSS, and JavaScript only. To store the list of songs, I have created an array named "songs" with details such as song name, file path, and cover image path. let songs = [ {songNa ...

Why are the UI components (`Card Number`, `MM/YY`, `CVC`) not being displayed in the React app when using Card

Having an issue where the CardElement Ui component is not visible in the image provided. It should appear above the Order Total: $0 next to the payment method. https://i.sstatic.net/NCK5z.png I've attempted various debugging methods without success. ...

"Encountered a TypeError: Cannot read property 'params

I've encountered an issue with passing the id to my product page. Despite trying various solutions and searching for answers, I still can't get it to work. Below is my index.js code: import React from "react"; import {render} from &quo ...