Incorporating reactive form validation for an Angular application with the use of the <input type="file"> input field

I am in the process of developing a component that includes a file picker for uploading files to our CDN. I am working on integrating a reactive form into this component to validate the image input, allowing me to verify against file name and extension among other criteria. By encapsulating it within a form, I can leverage Angular's validation capabilities.

The HTML code for my component is as follows:

<form class="mt-4" [formGroup]="form">
  <div class="form-group form-inline">
    <label class="btn btn-secondary btn-file">Browse
       <input name="file" type="file" (change)="onChange($event)" formControlName="imageInput"
    </label>

    <p *ngIf="file" class="pl-4 align-middle mb-0">{{file.name}}</p>
  </div>

  <button type="button" class="btn btn-primary">Upload</button>
</form>

As for the code behind the component:

onChange(event: EventTarget) {
  // File picker logic

  this.form = this.formBuilder.group({
      imageInput: [this.file.name, CustomValidators.imageValidator]
  });
}

The CustomValidators.imageValidator currently just logs the input value.

Upon loading the component, an error message is displayed:

ERROR DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

In essence, my goal is to utilize the file input within my reactive form to perform validations based on the filename.

Answer №1

While it hasn't been implemented yet according to @ibrahim, I encountered the same issue and resolved it by utilizing a hidden field. In the onFileChange method, assign the value of file.name to the hidden field for validation purposes.

<form class="mt-4" [formGroup]="form">
  <div class="form-group form-inline">
    <label class="btn btn-secondary btn-file">Browse
      <input name="file" type="file" (change)="onFileChange($event)">
      <input type="hidden" name="fileHidden" formControlName="imageInput"/> <!-- Validation Field -->
    </label>

    <p *ngIf="file" class="pl-4 align-middle mb-0">{{file.name}}</p>
  </div>
  <button type="button" class="btn btn-primary">Upload</button>
</form>

onFileChange($event) {
  let file = $event.target.files[0]; // <--- File Object for future use.
  this.form.controls['imageInput'].setValue(file ? file.name : ''); // <-- Set Value for Validation
}

fileName = '';
this.form = this.formBuilder.group({
  imageInput: [fileName, Validators.required]
});

Answer №2

export class customClass implements OnInit {

  myForm = new FormGroup({
    username: new FormControl(''),
    selectedFile: new FormControl('')
  })

  onChangeFile($event) {

    this.myForm.patchValue({
      selectedFile: $event.target.files[0]
    })
  }

}
<input type="file" (change)="onChangeFile($event)">

Answer №3

The value attribute of the <input type="file"> element is set as read-only. To prevent an error, ensure you include the option emitModelToViewChange:false when using the setValue() function:

An ERROR DOMException occurs when attempting to update the 'value' property of the 'HTMLInputElement'. This input only accepts a file name and must be programmatically set to an empty string.

Here is the solution :

this.form.get('<name>').setValue(file, {emitModelToViewChange: false});

Answer №4

To fix the issue, simply take out the

formControlName="imageInput"
attribute from your

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

Tips for determining the overall percentage breakdown of 100% based on the individual denominator for every column within angular 8

In my code, I have a simple function that calculates the sum of numbers and strings in columns within a table. The sum calculation itself works fine and provides accurate results. However, the problem arises when I attempt to divide the total sum of each c ...

Tips for modifying the type definition of a third-party library in a Vue project built with Create-Vue

After updating the Cesium library in my Vue project, I encountered some errors. This is the code snippet: camera.setView({ destination, orientation: { heading, pitch } }) The error message reads: Type '{ heading: number; pitch: number; }' i ...

Steering clear of Endless Loops in Spring Boot

When utilizing a Spring Boot Rest API, I successfully prevented an infinite loop by using @JsonIgnore. However, in the postman response, the related list on the many side appears as null. How can I go about displaying this related list in Angular even if i ...

Sanitizing HTML using the Ionic DomSanitizer may not be effective in bypassing security for trusted resource URLs

Encountering an error message that says GET http://localhost:8100/null 404 (Not Found) I've experimented with various DomSanitizer methods, but so far none of them have resolved the issue. The function in my controller is as follows: import { DomSan ...

How can I move the cursor to the beginning of a long string in Mat-autocomplete when it appears at the end?

I'm struggling to figure out how to execute a code snippet for my Angular app on Stack Overflow. Specifically, I am using mat-autocomplete. When I select a name that is 128 characters long, the cursor appears at the end of the selected string instead ...

I am attempting to gather user input for an array while also ensuring that duplicate values are being checked

Can someone assist me with the following issue: https://stackblitz.com/edit/duplicates-aas5zs?file=app%2Fapp.component.ts,app%2Fapp.component.html I am having trouble finding duplicate values and displaying them below. Any guidance would be appreciated. I ...

Compelled to utilize unfamiliar types in TypeScript generics

When working with a Typescript React form builder, I encountered a situation where each component had different types for the value and onChange properties. To tackle this issue, I decided to utilize generics so that I could define the expected types for e ...

Check the status when clicking on an event using Angular Material

I am working with an Angular element that involves a checkbox. <mat-checkbox class="btn-block" labelPosition="before" (change)="showOptions($event)" (click)="makeJSON($event.checked,i,j,k)"> </mat-chec ...

"Jest test.each is throwing errors due to improper data types

Currently, I am utilizing Jest#test.each to execute some unit tests. Below is the code snippet: const invalidTestCases = [ [null, TypeError], [undefined, TypeError], [false, TypeError], [true, TypeError], ]; describe('normalizeNames', ...

What is the process for expanding types for a section element in Typescript?

When working with TypeScript, we define our component props types by extending a div like so: import { HTMLAttributes } from "react"; export interface IContainerProps extends HTMLAttributes<HTMLDivElement> { // Additional custom props for the c ...

Steps to define attributes within an Angular constructor for successful resolution

I am currently facing an issue with the "Can't resolve all parameters..." error when adding properties to my constructor. It seems to be a recurring problem for me, indicating that I may have a foundational misunderstanding of how this process operate ...

Oops! The program encountered a TypeError stating that it cannot read the property 'open' since it is undefined

Using Angular 6, I have implemented an API call in a service and subscribed to it in my controller. The data is successfully received, but I want to restructure it in the controller so that I can later pass it into a Chart JS chart. Here is the code snipp ...

Efficiently convert Map keys into a Set in Javascript without the need to copy and rebuild the set

Even though I am capable of const set = new Set(map.keys()) I don't want to have to rebuild the set. Additionally, I prefer not to create a duplicate set for the return value. The function responsible for returning this set should also have the abili ...

Display the autocomplete dropdown in Angular 2+ only once the user has typed at least one letter

Looking to create a customized autocomplete feature using Angular 2+ and angular material design, where the options are only displayed after at least one letter has been typed. The default behavior of the autocomplete is to toggle on focus even when the i ...

Issue: Unable to assign value to 'googleUri' property of null. Resolving with Interface for two-way binding?

Can anyone help me figure out why I keep getting a 'set property of null' error while attempting 2way binding in my interface? Whenever I submit the form and trigger the onSave function, I encounter the error "Cannot set property 'googleUri ...

Passing a class as a parameter in Typescript functions

When working with Angular 2 testing utilities, I usually follow this process: fixture = TestBed.createComponent(EditableValueComponent); The EditableValueComponent is just a standard component class that I use. I am curious about the inner workings: st ...

Using the 'disabled' parameter in the form state object does not have any effect on the FormControl constructor

We've encountered a similar issue in the past: It seems like the disabled attribute is being used with a reactive form directive. If you initialize this control with the disabled property set to true in your component class, Angular will automatical ...

Angular 8 @viewChild directive resulting in a null value

stripeService.ts @ViewChild('cardElementForm', { static: false }) cardElementForm: ElementRef; stripe = Stripe(environment.stripe.pubKey); async initStripeElements() { const elements = this.stripe.elements(); const cardElement = e ...

React's useState hook fails to correctly update object values

Having an issue with updating a state variable with an object in React. The code is as follows: async function getServers() { console.log("ready") const response = await fetch('http://localhost:3000/server/servers').th ...

What is the proper way to send a list as a parameter in a restangular post request

Check out this code snippet I found: assignUserToProject(pid: number, selectedUsers: any, uid: number) { let instance = this; return instance.Restangular.all("configure/assign").one(pid.toString()).one(uid.toString()).post(selectedUsers); } ...