Learn how to build forms in Aurelia, bind data to various input elements, and handle submission and validation.
Handling forms and user input is a common task in most applications. Whether you are building a login form, a data-entry screen, or even a chat interface, Aurelia makes it intuitive to work with forms. By default, Aurelia’s binding system uses two-way binding for form elements (like <input>, <textarea>, and contenteditable elements), which keeps your view and model in sync automatically.
Many of the concepts discussed here assume some familiarity with Aurelia’s binding and template syntax. If you’re new, please read the Template Syntax & Features section first.
Data Flow in Forms
Aurelia’s two-way binding updates your view model properties whenever users enter data into form elements, and likewise updates the form elements if the view model changes:
The user types in the input (e.g., John).
The native input events fire. Aurelia observes the value change.
The binding system updates the corresponding view model property.
Any references to that property automatically reflect its new value.
Because of this automatic synchronization, you generally don’t need to write custom event handlers or watchers to track form inputs.
Creating a Basic Form
Aurelia lets you create forms in pure HTML without any special setup. Here’s a simple login form illustrating how little code is required.
We created a form with two inputs: email and password.
The value.bind syntax binds these inputs to class properties named email and password.
We call a handleLogin() method on submit to process the form data.
And here is the view model (login-component.ts):
login-component.ts
export class LoginComponent {
private email = "";
private password = "";
handleLogin() {
// Validate credentials or call an API
console.log(`Email: ${this.email}, Password: ${this.password}`);
}
}
Whenever the email or password fields change in the UI, their corresponding view model properties are updated. Then, in handleLogin(), you can handle form submission however you wish.
Using submit.trigger on a form prevents the default browser submission. If you want the form to submit normally, return true from your handler or remove submit.trigger entirely.
Binding With Text and Textarea Inputs
Text Input
Binding to text inputs in Aurelia is straightforward:
Typically, a <form> groups related inputs. Aurelia allows you to intercept submission using submit.trigger:
<form submit.trigger="submitMyForm()">...</form>
export class MyApp {
submitMyForm() {
// Custom logic, e.g., fetch POST to an API endpoint
fetch("/register", { method: "POST" /* ... */ });
}
}
For <form> elements without a method (or method="GET"), Aurelia automatically calls event.preventDefault() to avoid a full page reload. If you prefer the default browser submission, return true from your handler:
export class MyApp {
submitMyForm() {
// Possibly do some checks...
return true; // Allow normal form submission
}
}
File Inputs and Upload Handling
Working with file uploads in Aurelia typically involves using the standard <input type="file"> element and handling file data in your view model. While Aurelia doesn’t provide special bindings for file inputs, you can easily wire up event handlers or use standard properties to capture and upload files.
Capturing File Data
In most cases, you’ll want to listen for the change event on a file input:
accept="image/*": Restricts file selection to images (this can be changed to fit your needs).
change.trigger="handleFileSelect($event)": Calls a method in your view model to handle the file selection event.
View Model Handling
You can retrieve the selected files from the event object in your view model:
file-upload-component.ts
export class FileUploadComponent {
public selectedFiles: File[] = [];
public handleFileSelect(event: Event) {
const input = event.target as HTMLInputElement;
if (!input.files?.length) {
return;
}
// Convert the FileList to a real array
this.selectedFiles = Array.from(input.files);
}
public async uploadFiles() {
if (this.selectedFiles.length === 0) {
return;
}
const formData = new FormData();
for (const file of this.selectedFiles) {
// The first argument (key) matches the field name expected by your backend
formData.append('files', file, file.name);
}
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Upload failed with status ${response.status}`);
}
const result = await response.json();
console.log('Upload successful:', result);
// Optionally, reset selected files
this.selectedFiles = [];
} catch (error) {
console.error('Error uploading files:', error);
}
}
}
Key Points:
Reading File Data: input.files returns a FileList; converting it to an array (Array.from) makes it easier to iterate over.
FormData: Using FormData to append files is a convenient way to send them to the server (via Fetch).
Error Handling: Always check response.ok to handle server or network errors.
Disabling the Button: In the HTML, disabled.bind="!selectedFiles.length" keeps the button disabled until at least one file is selected.
Single File Inputs
If you only need a single file, omit multiple and simplify your logic:
public handleFileSelect(event: Event) {
const input = event.target as HTMLInputElement;
this.selectedFiles = input.files?.length ? [input.files[0]] : [];
}
Validation and Security
When handling file uploads, consider adding validation and security measures:
Server-side Validation: Even if you filter files by type on the client (accept="image/*"), always verify on the server to ensure the files are valid and safe.
File Size Limits: Check file sizes either on the client or server (or both) to prevent excessively large uploads.
Progress Indicators: For a better user experience, consider using XMLHttpRequest or the Fetch API with progress events (via third-party solutions or polyfills), so you can display an upload progress bar.
Form Validation
Validation is essential for robust, user-friendly forms. Aurelia provides a dedicated Validation plugin that helps you:
Validate inputs using built-in or custom rules.
Display error messages and warnings.
Integrate seamlessly with Aurelia’s binding system.