See how Aurelia's elegant syntax compares to other popular frameworks and libraries. This is just a basic comparison, but it should give you a good idea of how Aurelia compares to other frameworks and libraries.
Conditional rendering allows you to render different content based on the state of your application.
<!-- Simple if binding -->
<div if.bind="isVisible">Hello World</div>
<!-- If/else rendering -->
<div if.bind="isAuthenticated">
Welcome back, ${username}!
</div>
<div else>
Please log in
</div>
Two-way binding allows you to bind a property to an input element and have the property update when the input changes.
<!-- Two-way binding -->
<input type="text" value.bind="username">
<p>Hello, ${username}!</p>
List rendering allows you to render a list of items.
<!-- List rendering with repeat.for -->
<ul>
<li repeat.for="item of items">${item.name}</li>
</ul>
Event handling allows you to handle events from the DOM.
<!-- Simple click event -->
<button click.trigger="handleClick()">Click me</button>
<!-- Event with parameter -->
<button click.trigger="handleClick($event)">With Event</button>
<!-- Key events -->
<input keyup.trigger="handleKeyUp($event)">
Class and style binding allows you to bind classes and styles to an element.
<!-- Class binding -->
<div class.bind="dynamicClass">Dynamic class</div>
<div class="${condition ? 'active' : 'inactive'}">Conditional</div>
<!-- Style binding -->
<div style.bind="dynamicStyles">Dynamic styles</div>
<div style="color: ${textColor}">Dynamic color</div>
Component creation allows you to create a new component.
// my-component.ts
@customElement({
name: 'my-component',
template: `
<h1>\${message}</h1>
<button click.trigger="sayHello()">Greet</button>
`
})
export class MyComponent {
message = 'Hello World';
@bindable name: string;
sayHello() {
console.log(`Hello ${this.name}!`);
}
}
Computed properties are properties that are derived from other properties. They are cached and only re-evaluated when their dependencies change.
export class MyComponent {
firstName = 'John';
lastName = 'Doe';
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
<!-- Template -->
<div>${fullName}</div>
Template references allow you to reference elements in the template from the component definition without needing to query the DOM.
<!-- Template reference -->
<input ref="nameInput" type="text">
<button click.trigger="focusInput()">Focus</button>
// Component class
export class MyComponent {
nameInput: HTMLInputElement;
focusInput() {
this.nameInput.focus();
}
}
Lifecycle hooks allow you to run code at specific stages of a component's lifecycle, such as when it mounts or unmounts.
export class MyComponent {
data = null;
attached() {
// Runs when component is attached to DOM
this.data = fetchData();
}
detaching() {
// Runs before component is removed from DOM
cleanup();
}
}
Props allow parent components to pass data to child components, while events allow child components to communicate back to parents.
// child-component.ts
import { bindable, EventAggregator } from 'aurelia';
export class ChildComponent {
@bindable message: string;
@bindable onUpdate: (value: string) => void;
handleClick() {
// Emit event to parent
this.onUpdate?.('Updated value');
}
}
<!-- Parent usage -->
<child-component
message.bind="parentMessage"
on-update.call="handleUpdate($event)">
</child-component>
Watchers allow you to perform side effects in response to data changes.
import { watch } from 'aurelia';
export class MyComponent {
username = '';
@watch('username')
usernameChanged(newValue, oldValue) {
console.log(`Username changed from ${oldValue} to ${newValue}`);
// Perform side effect
validateUsername(newValue);
}
}
Slots allow parent components to pass content into child components, enabling flexible and reusable component designs.
// card.html
<template>
<div class="card">
<div class="card-header">
<au-slot name="header">Default Header</au-slot>
</div>
<div class="card-body">
<au-slot>Default content</au-slot>
</div>
</div>
</template>
<!-- Usage -->
<card>
<div au-slot="header">Custom Header</div>
<p>Custom content goes here</p>
</card>
Form handling and validation patterns for user input.
import { newInstanceOf } from 'aurelia';
import { ValidationController, ValidationRules } from '@aurelia/validation';
export class MyForm {
email = '';
password = '';
constructor(
@newInstanceOf(ValidationController) private controller: ValidationController
) {
ValidationRules
.ensure('email').required().email()
.ensure('password').required().minLength(8)
.on(this);
}
async submit() {
const result = await this.controller.validate();
if (result.valid) {
// Process form
}
}
}
<!-- Template -->
<form submit.trigger="submit()">
<input type="email" value.bind="email & validate">
<input type="password" value.bind="password & validate">
<button type="submit">Submit</button>
</form>