Angular File Upload with Web API – Guide for Different Angular Versions

This document provides code examples for implementing file upload functionality in Angular applications with a Web API backend, covering different Angular versions.

HTML Template (Same for all versions)

Copy to clipboard
<input type="file" (change)="onFileSelected($event)" multiple="">
<button (click)="uploadFiles()">Upload</button>
<progress [value]="uploadProgress" max="100"></progress>

Angular 17 (Latest)

Copy to clipboard
@Component({ standalone: true, imports: [CommonModule] })
export class FileUploadComponent {
  private http = inject(HttpClient);
  selectedFiles: File[] = [];
  uploadProgress = 0;

  onFileSelected(event: Event) {
    const input = event.target as HTMLInputElement;
    this.selectedFiles = input.files ? Array.from(input.files) : [];
  }
  uploadFiles() {
    const formData = new FormData();
    this.selectedFiles.forEach(file => formData.append('files', file));

    this.http.post('/api/upload', formData, {
      reportProgress: true,
      observe: 'events'
    }).subscribe({
      next: (event) => {
        if (event.type === HttpEventType.UploadProgress && event.total) {
          this.uploadProgress = Math.round((100 * event.loaded) / event.total);
        }
      },
      error: (err) => console.error(err)
    });
  }
}

Angular 15-16

Copy to clipboard
@Component({ /* NgModule-based */ })
export class FileUploadComponent {
  constructor(private http: HttpClient) {}
  selectedFiles: File[] = [];

  onFileSelected(event: any) {
    this.selectedFiles = Array.from(event.target.files);
  }
  uploadFiles() {
    const formData = new FormData();
    this.selectedFiles.forEach(file => formData.append('files', file));

    this.http.post('/api/upload', formData, {
      reportProgress: true,
      observe: 'events'
    }).subscribe({
      next: (event) => { /* Same as Angular 17 */ },
      error: (err) => console.error(err)
    });
  }
}

Angular 12-14

Copy to clipboard
@Component({ /* NgModule-based */ })
export class FileUploadComponent {
  constructor(private http: HttpClient) {}
  selectedFiles: FileList | null = null;
  onFileSelected(event: any) {
    this.selectedFiles = event.target.files;
  }
  uploadFiles() {
    if (!this.selectedFiles) return;
    const formData = new FormData();
    Array.from(this.selectedFiles).forEach(file => formData.append('files', file));

    this.http.post('/api/upload', formData, {
      reportProgress: true,
      observe: 'events'
    }).subscribe(
      (event) => { /* Same progress handling */ },
      (err) => console.error(err)
    );
  }
}

Angular 2-11

Copy to clipboard
@Component({ /* NgModule-based */ })
export class FileUploadComponent {
  constructor(private http: HttpClient) {}
  selectedFiles: FileList | null = null;

  onFileSelected(event: any) {
    this.selectedFiles = event.target.files;
  }
  uploadFiles() {
    if (!this.selectedFiles) return;
    const formData = new FormData();
    for (let i = 0; i < this.selectedFiles.length; i++) {
      formData.append('files', this.selectedFiles[i]);
    }
    this.http.post('/api/upload', formData, {
      reportProgress: true,
      observe: 'events'
    }).subscribe(
      (event) => { /* Same progress handling */ },
      (err) => console.error(err)
    );
  }
}

Key Differences

1. Angular 17 (Latest)

  • Standalone Components: No NgModule required. Uses standalone: true.
  • Modern Injection: inject(HttpClient) replaces constructor-based DI.
  • Strict Typing: Event targets use type assertions (as HTMLInputElement).
  • Observable Syntax: Uses subscribe({ next, error }) for cleaner error handling.

Code Impact:

Copy to clipboard
// Component definition
@Component({ standalone: true, imports: [CommonModule] })

// File selection (strict typing)
onFileSelected(event: Event) {
  const input = event.target as HTMLInputElement;
  this.selectedFiles = input.files ? Array.from(input.files) : [];
}

2. Angular 15-16

  • Optional Standalone: Supports both NgModule and (from v15+) standalone.
  • Improved HTTP Client: Stronger type inference for HttpEventType.
  • Null Safety: Stricter checks for FileList handling.

Code Impact:

Copy to clipboard
// Uses constructor DI (like older versions)
constructor(private http: HttpClient) {}

// FileList conversion (explicit null checks)
if (!event.target.files) return;
this.selectedFiles = Array.from(event.target.files);

3. Angular 12-14

  • Strict Mode Default: Forces null checks (strictTemplates, strictNullChecks).
  • Ivy Renderer: Better performance but may break legacy View Engine code.
  • Modern Iteration: Prefers Array.from() over for loops.

Code Impact:

Copy to clipboard
// Mandatory null checks
selectedFiles: FileList | null = null;

// Safer event handling
if (event.type === HttpEventType.UploadProgress && event.total) { 
  // TypeScript knows `event.total` exists
}

4. Angular 2-11 (Legacy)

  • View Engine: Older rendering pipeline (slower, less optimized).
  • Loose Typing: Frequent use of any for events (event: any).
  • Manual Checks: No strict mode; runtime errors possible.

Code Impact:

Copy to clipboard
// Legacy FileList handling
selectedFiles: FileList; // No strict null checks

// Manual type assertions for HTTP progress
if (event.type === HttpEventType.UploadProgress) {
  const progress = (event.loaded / (event as any).total) * 100;
}

Need Help With Angular Development?

Work with our skilled Angular developers to accelerate your project and boost its performance.

Support On Demand!