Inject a DOM event into a personalized form validator within an Angular application

I'm currently working on validating a form using the reactive approach. I've implemented a file input to allow users to upload files, with custom validation conditions in place. However, I'm encountering an issue where the validator only receives the file path (e.g., C:\fakepath\abc.xlsx) instead of the full event object. I need access to all file properties like type and size within the validator function.

Here's the relevant code snippet:

file.validator.ts

import { AbstractControl } from '@angular/forms';

export function ValidateFile(control: AbstractControl) : 
{ [key: string]: boolean } | null {
    const value = control.value;

    if (!value) {
        return null;
    }

    return value.length < 0 && value.files[0].type !== '.xlsx' && value.files[0].size > 5000000
    ? { invalidFile: true } : null;

}

sheet.component.ts

constructor(
private formBuilder: FormBuilder,
private alertService: AlertService
) {
    this.sheetForm = this.formBuilder.group({
    sheetType: ['Select Sheet Type', [Validators.required]],
    sheetUpload: [null, [Validators.required, ValidateFile]],
    sheetDescription: [
      null,
      [
        Validators.required,
        Validators.minLength(10),
        Validators.maxLength(100),
      ],
    ],
  });
}

sheet.component.html

<div class="input-group">
    <label for="sheet-upload">Upload Sheet: </label> &nbsp; &nbsp;
    <input
      id="sheet-upload"
      type="file"
      (change)="handleFileInput($event)"
      formControlName="sheetUpload"
      accept=".xlsx"
    />
    <small
      id="custom-error-message"
      *ngIf="
        (sheetForm.get('sheetUpload').dirty ||
          sheetForm.get('sheetUpload').touched) &&
        sheetForm.get('sheetUpload').invalid
      "
    >
      The file size exceeds 5 MB or isn't a valid excel type. Please
      upload again.
    </small>
</div>

Any advice or guidance would be highly appreciated. Thank you!

Answer №1

There may be other methods, but this one gets the job done

  • Implement a directive to connect the native element to the form control
  • Retrieve the file from the native element within the validator function during validation
  • To utilize formControlName, ensure that a formGroup is assigned to the parent element (unless it's already included in another parent element)
@Directive({
  selector: '[formControlName]',
})
export class NativeElementInjectorDirective implements OnInit {
  constructor(private el: ElementRef, private control: NgControl) {}

  ngOnInit() {
    (this.control.control as any).nativeElement = this.el.nativeElement;
  }
}

file.validator.ts

export function ValidateFile(control: any): { [key: string]: boolean } | null {
  const value = control.value;
  const file = control?.nativeElement?.files[0];

  if (!value) {
    return null;
  }

  return value.length < 0 || file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.size > 5000000
    ? { invalidFile: true }
    : null;
}

sheet.component.html

<div class="input-group" [formGroup]="sheetForm">
  <label for="sheet-upload">Upload Sheet: </label> &nbsp; &nbsp;
  <input
    id="sheet-upload"
    type="file"
    formControlName="sheetUpload"
    accept=".xlsx"
  />
  <small
    id="custom-error-message"
    *ngIf="
      (sheetForm.get('sheetUpload').dirty ||
        sheetForm.get('sheetUpload').touched) &&
      sheetForm.get('sheetUpload').invalid
    "
  >
    The file size exceeds 5 MB or isn't a valid excel type. Please upload again.
  </small>
</div>

Answer №2

If you want to validate an input element, make sure to reference it and utilize it in the validator function.

<input #sheetUpload ...>

@ViewChild('sheetUpload') fileInput: HTMLInputElement;

private ValidateFile(): ValidatorFn {
return (control) => {
  const value = control.value;

  if (!value || !this.fileInput) {
    return null;
  }

  const file = this.fileInput.files[0];

  return value.length < 0 && file.type !== '.xlsx' && file.size > 5000000
    ? { invalidFile: file.name }
    : null;
  }
}

Similar questions

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

What could be the reason for not receiving any response from my Firestore query?

Hey there! I'm delving into the world of Firebase for the first time and just set up the Firestore emulator. I've added some data that I want to fetch in my Nextjs app. Once I initialized firebase, this is what my component code looks like: funct ...

NodeJS: The module failed to automatically register itself

Exploring the capabilities of IBM Watson's Speech to Text API, I encountered an issue while running my NodeJS application. To handle the input audio data and utilize IBM Watson's SpeechToText package, I integrated the line-in package for streami ...

The FileReader's onload event handler does not seem to be triggering as expected

In short, my issue revolves around reading a csv file from an android device using JavaScript's FileReader. Although my code was functioning properly a month ago, upon revisiting it recently I discovered that the onload function no longer seems to be ...

I'm seeking clarity on the proper replacement for ngModel within this Angular code, as I've been cautioned about using form control name and ngModel simultaneously

I have been using ngModel and formControlName together, which is causing a warning to appear in the console. I am trying to resolve this issue by removing ngModel, but I'm unsure of what to replace it with. I've attempted a few solutions, but non ...

Techniques for sending PHP variables to window.location using JavaScript

How can I successfully include a PHP variable in a JavaScript window.location function? The current code snippet below does not seem to be working for me. echo '<script>location.href = "reportConsumption.php?creategenReport="'.$genid.&apos ...

Using JavaScript/JQuery, change relative or viewport sizes to fixed sizes when the page loads

Wishing you a delightful day. As I work on my website, I find myself relying heavily on viewport units like vw and vh for all measurements such as font size, padding, margin, width, and height. These units provide the flexibility needed to ensure that the ...

Deactivate user input in Knockout Kendo TimePicker

Is it possible to prevent user input in the Kendo UI TimePicker while using knockout-kendo binding? In a previous project without knockout-kendo, I was able to achieve this by using the following code (see jsfiddle example): $('#timepicker').at ...

What's causing jQuery to make the entire page malfunction?

I am experiencing an issue where a function is not recognized as being in scope when I have a reference to jQuery defined. Oddly enough, if I comment out that reference, the function call works just fine. I have other pages set up the same way with jQuer ...

Show a caution message when there has been no activity on the page for 3 minutes

I am currently developing a PHP page that includes timed tests. The maximum duration for a test is 1 hour, however the session times out after just 10 minutes of inactivity. Interestingly, when the timeout occurs, the test page does not automatically refre ...

Updating a document on Firestore based on a query is a straightforward process that involves first identifying

I'm currently working on a web form page that needs to update input fields into a specific firestore document based on certain conditions. Can anyone provide guidance on how this can be achieved? The initial part where I retrieve the query results se ...

Exploring URL Parameters in Angular Unit Testing

My goal is to execute a test to check for the presence of a specific string in URL parameters. Inside my TypeScript file, I have defined the following method: checkURLParams() { if (this.route.parent) { this.route.parent.params.subscribe((params) ...

difficulty receiving the information promptly via an AJAX request (utilizing AJAX and the 'for' loop)

Currently, I am successfully retrieving data from an ajax call for individuals. However, my next task is to retrieve multiple sets of data simultaneously. Here is the code snippet: for(var i = 1; i <= 2; i++){ console.log(i); $.ajax({ url: cal ...

How can TypeScript leverage the power of JavaScript libraries?

As a newcomer to TypeScript, I apologize if this question seems simplistic. My goal is to incorporate JavaScript libraries into a .ts file while utilizing node.js for running my program via the console. In my previous experience with JavaScript, I utilize ...

What is the best approach for adding variable rows to a Postgres junction table: should you concatenate a query string, utilize multiple queries, or explore alternative methods

Below is the code snippet for handling a variable-length list of tags and inserting data into the database: // JSON object from req.body { "title": "title", "reference": "1213", "noteType": &q ...

How to Use Attributes as Component Parameters in Vue.js

I am currently developing a test Component using Vue.js. I am trying to pass a parameter to be used in my template like this: Vue.component('test', { props: ['href'], template: '<li><a href="{{href}}"><slot> ...

Choosing the initial object in Angular2 using ngValue

Currently, I am encountering an issue where the initial value is not being set or updated when populating a select element with data from a REST source within my component. To populate the select list, I retrieve the country data as follows: ... ngOnInit ...

Struggling to form an array of arrays: encountering an issue with data.map not being a function

I have received some data in the following format: const mockData = [ { "1": [ { val1: 0.9323809524, val2: 5789.12, val3: 84.467, val4: 189.12, val5: 8, bins: 1, }, { ...

What steps can you take to stop a tab from being inserted if one is already present?

I am facing a simple issue where I need to prevent the insertion of a tab if one already exists. Issue: I have a search bar that displays results in a div with class.result_container_2 when a user inputs a name. Upon clicking on this tab, another tab is i ...

Utilizing const as the iteration variable in a for loop

I've grasped the concept of using var and let in a for loop in typescript/javascript, but can someone shed light on how and why a const variable as a loop variable behaves? for (const i = 0; i < 5; i++) { setTimeout(function() { console.log( ...

Display JSON on the screen so it can be easily copied and pasted

I have a unique challenge where I need to output some Javascript code on the browser screen for easy transfer to another program. Currently, I am utilizing JSON.stringify() from the json2.js library. However, this method is not correctly escaping characte ...