Skip to main content

Angular Progress Events

When working with large files or data transfers in Angular applications, it's often crucial to provide users with feedback about the progress of these operations. Angular's HTTP client includes built-in support for tracking request and response progress through Progress Events. This feature is particularly useful when implementing file uploads, downloads, or any operation where the user benefits from seeing real-time progress information.

Understanding Progress Events

Progress events allow you to monitor the status of HTTP requests as they're being processed. In Angular, these events are available through the HttpClient module and can be enabled using the reportProgress option.

Types of Progress Events

Angular's HTTP client can emit different types of progress events:

  1. Download Progress - Tracks the progress of receiving data from the server
  2. Upload Progress - Tracks the progress of sending data to the server

Setting Up Progress Event Monitoring

To start tracking progress events, we need to:

  1. Import the necessary modules
  2. Configure the HTTP request with the reportProgress and observe options
  3. Subscribe to the events and handle them appropriately

Let's see how to set this up:

typescript
import { HttpClient, HttpEventType, HttpResponse } from '@angular/common/http';

@Component({
selector: 'app-file-upload',
templateUrl: './file-upload.component.html'
})
export class FileUploadComponent {
uploadProgress: number = 0;

constructor(private http: HttpClient) {}

uploadFile(file: File) {
const formData = new FormData();
formData.append('file', file);

this.http.post('https://api.example.com/upload', formData, {
reportProgress: true, // Enable progress events
observe: 'events' // Observe the full HTTP events, not just the response body
}).subscribe(event => {
this.handleEvent(event);
});
}

private handleEvent(event: any) {
switch (event.type) {
case HttpEventType.UploadProgress:
// Calculate and update progress percentage
this.uploadProgress = Math.round(100 * event.loaded / event.total);
break;

case HttpEventType.Response:
// Upload complete
console.log('Upload complete', event.body);
break;
}
}
}

Displaying Progress in the Template

You can display this progress in your template with a progress bar:

html
<div *ngIf="uploadProgress > 0 && uploadProgress < 100">
<p>Upload Progress: {{ uploadProgress }}%</p>
<div class="progress">
<div class="progress-bar"
[style.width.%]="uploadProgress"></div>
</div>
</div>

<div *ngIf="uploadProgress === 100">
Upload complete!
</div>

Understanding HttpEvent Types

Angular's HTTP client emits various event types during a request. The HttpEventType enum defines these types:

typescript
enum HttpEventType {
Sent = 0, // The request was sent
UploadProgress = 1, // Upload progress event
ResponseHeader = 2, // Response headers received
DownloadProgress = 3, // Download progress event
Response = 4, // Full response received
User = 5 // Custom event
}

When working with progress events, you'll primarily use UploadProgress, DownloadProgress, and Response.

Tracking Download Progress

Download progress tracking works similarly to upload progress. Here's how to implement it:

typescript
downloadFile(fileId: string) {
this.downloadProgress = 0;

this.http.get(`https://api.example.com/files/${fileId}`, {
reportProgress: true,
observe: 'events',
responseType: 'blob' // Important for file downloads
}).subscribe(event => {
if (event.type === HttpEventType.DownloadProgress) {
this.downloadProgress = Math.round(100 * event.loaded / (event.total || 1));
} else if (event.type === HttpEventType.Response) {
// Download complete - save the file
const blob = new Blob([event.body], { type: event.headers.get('content-type') || 'application/octet-stream' });
const url = window.URL.createObjectURL(blob);

// Create a download link and click it
const a = document.createElement('a');
a.href = url;
a.download = 'downloaded-file';
document.body.appendChild(a);
a.click();

// Clean up
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
});
}

Real-World Example: File Upload with Progress and Cancel Option

Let's build a more complete file upload component with progress tracking and the ability to cancel uploads:

typescript
import { Component } from '@angular/core';
import { HttpClient, HttpEventType, HttpRequest, HttpResponse } from '@angular/common/http';
import { finalize } from 'rxjs/operators';

@Component({
selector: 'app-file-upload',
template: `
<div class="upload-container">
<input type="file" (change)="onFileSelected($event)" [disabled]="uploading">

<div *ngIf="uploading" class="progress-container">
<div class="progress">
<div class="progress-bar" [style.width.%]="uploadProgress"></div>
</div>
<span>{{ uploadProgress }}%</span>
<button (click)="cancelUpload()">Cancel</button>
</div>

<div *ngIf="uploadComplete" class="success-message">
Upload complete!
</div>
</div>
`,
styles: [`
.progress-container {
margin-top: 20px;
}
.progress {
height: 20px;
background-color: #f5f5f5;
border-radius: 4px;
margin-bottom: 10px;
}
.progress-bar {
height: 100%;
background-color: #007bff;
border-radius: 4px;
transition: width 0.3s ease;
}
.success-message {
color: green;
margin-top: 20px;
}
`]
})
export class FileUploadComponent {
uploadProgress: number = 0;
uploading: boolean = false;
uploadComplete: boolean = false;
private uploadSubscription: any;

constructor(private http: HttpClient) {}

onFileSelected(event: any) {
const file = event.target.files[0];
if (file) {
this.uploadFile(file);
}
}

uploadFile(file: File) {
this.uploading = true;
this.uploadProgress = 0;
this.uploadComplete = false;

// Create form data
const formData = new FormData();
formData.append('file', file);

// Create a request object that we can use with the subscription
const req = new HttpRequest('POST', 'https://api.example.com/upload', formData, {
reportProgress: true
});

// Send the request and store the subscription
this.uploadSubscription = this.http.request(req).pipe(
finalize(() => {
this.uploading = false;
})
).subscribe(
(event: any) => {
if (event.type === HttpEventType.UploadProgress) {
this.uploadProgress = Math.round(100 * event.loaded / event.total);
} else if (event instanceof HttpResponse) {
this.uploadComplete = true;
console.log('Upload successful!', event.body);
}
},
error => {
console.error('Upload failed:', error);
this.uploading = false;
}
);
}

cancelUpload() {
if (this.uploadSubscription) {
this.uploadSubscription.unsubscribe();
this.uploading = false;
this.uploadProgress = 0;
console.log('Upload canceled');
}
}
}

In this example:

  1. We track the upload state with multiple variables (uploading, uploadProgress, uploadComplete)
  2. We use HttpRequest to create a request object that we can reference
  3. We store the subscription to allow for cancellation
  4. We use the finalize operator to ensure cleanup when the request completes, errors, or is cancelled
  5. The UI updates in real-time as the upload progresses

Handling Large File Uploads Efficiently

When dealing with large file uploads, consider these best practices:

  1. Server Configuration - Ensure your server is configured to handle large file sizes
  2. Chunked Uploads - For very large files, consider splitting them into chunks
  3. Timeout Settings - Adjust timeout settings to accommodate longer uploads
  4. Error Handling - Implement retry logic for failed uploads
typescript
// Example of retry logic with exponential backoff
import { HttpClient } from '@angular/common/http';
import { retry, retryWhen, delay, scan, takeWhile } from 'rxjs/operators';

uploadWithRetry(file: File) {
const formData = new FormData();
formData.append('file', file);

return this.http.post('https://api.example.com/upload', formData, {
reportProgress: true,
observe: 'events'
}).pipe(
// Implement exponential backoff retry strategy
retryWhen(errors =>
errors.pipe(
// Track retry count
scan((retryCount, error) => {
if (retryCount >= 3) {
throw error; // Give up after 3 retries
}
return retryCount + 1;
}, 0),
// Delay for exponential backoff: 1s, 2s, 4s
delay(retryCount => Math.pow(2, retryCount) * 1000),
takeWhile(retryCount => retryCount < 3)
)
)
);
}

Summary

Angular's progress events provide a powerful way to enhance the user experience when working with HTTP requests that involve significant data transfer. Key points to remember:

  • Enable progress tracking with reportProgress: true and observe: 'events' options
  • Use HttpEventType to identify different stages of the HTTP request
  • Progress events include loaded and total properties to calculate percentage
  • Progress tracking works for both uploads and downloads
  • Subscriptions can be stored to enable cancellation of ongoing requests

With these techniques, you can build more responsive and user-friendly interfaces for file operations and data transfers in your Angular applications.

Additional Resources and Exercises

Resources

Exercises

  1. Basic Progress Bar: Create a simple file upload component that shows a progress bar during upload.

  2. Multiple File Upload: Extend the example to handle multiple file uploads simultaneously, each with their own progress indicator.

  3. Download with Progress: Implement a file download feature that shows a progress bar while downloading a large file.

  4. Advanced: Create a file manager component that supports both uploads and downloads with progress tracking, pause/resume functionality, and proper error handling.

  5. Challenge: Implement a chunked file upload system that splits large files into smaller pieces and shows overall progress as well as individual chunk progress.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)