LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Current route
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • Kernel Errors
      • Template Compiler Errors
      • Dialog Errors
      • Runtime HTML Errors
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • The repeat.for Binding
  • Keyed Iteration for Efficient DOM Updates
  • Syntax Examples
  • Contextual Properties in Repeats
  • Example Usage
  • Working with Arrays
  • Generating Ranges
  • Iterating Sets
  • Iterating Maps
  • Iterating Objects via Value Converters
  • Creating a Keys Value Converter
  • Using the Converter in a Template
  • Custom Collection Handling with Repeat Handlers
  • Custom Handler Example
  • Custom Handler Resolver
  • Summary

Was this helpful?

Export as PDF
  1. Templates

List Rendering

Learn how to work with collections of data like arrays and maps. The repeat.for functionality allows you to loop over collections of data and work with their contents similar to a Javascript for loop.

Aurelia’s templating system offers a robust way to work with collections—be they arrays, sets, maps, or even ranges. The repeat.for binding provides a declarative approach to iterating over data, creating scopes for each iteration, and optimizing DOM updates. This guide explains the intricacies of list rendering in detail.

The repeat.for Binding

At its core, repeat.for acts like a template-based for...of loop. It iterates over a collection and creates a new rendering context for each item. For example:

<ul>
  <li repeat.for="item of items">
    ${item.name}
  </li>
</ul>

This snippet tells Aurelia to:

  • Loop over each element in the items collection.

  • Assign the current element to a local variable named item.

  • Render the element (here, displaying item.name) for each iteration.

JavaScript Analogy:

for (let item of items) {
  console.log(item.name);
}

Keyed Iteration for Efficient DOM Updates

When working with dynamic collections, it’s crucial to minimize unnecessary DOM operations. Aurelia allows you to specify a unique key for each item in the collection. This key helps the framework:

  • Track Changes: By comparing key values, Aurelia can identify which items have been added, removed, or reordered.

  • Optimize Updates: Only the modified elements are updated, preserving performance.

  • Maintain State: DOM elements (e.g., with user input or focus) retain their state even if their order changes.

Syntax Examples

You can declare the key using either literal or binding syntax:

<!-- Literal syntax -->
<ul>
  <li repeat.for="item of items; key: id">
    ${item.name}
  </li>
</ul>

<!-- Bound syntax -->
<ul>
  <li repeat.for="item of items; key.bind: item.id">
    ${item.name}
  </li>
</ul>

Guidelines for Keys:

  • Uniqueness: Use a property that uniquely identifies each item (like an id).

  • Stability: Avoid using array indices if the collection order can change.

Contextual Properties in Repeats

Inside a repeat.for block, Aurelia exposes several contextual properties that give you more control over the rendering logic:

  • $index: The zero-based index of the current iteration.

  • $first: A boolean that is true on the first iteration.

  • $last: A boolean that is true on the final iteration.

  • $even / $odd: Flags indicating whether the current index is even or odd, which is useful for styling alternating rows.

  • $length: The total number of items in the collection.

  • $parent: A reference to the parent binding context. This is especially useful in nested repeaters.

Example Usage

<ul>
  <li repeat.for="item of items">
    Index: ${$index} — Name: ${item.name} <br>
    Is first? ${$first} | Is last? ${$last} <br>
    Even? ${$even} | Odd? ${$odd} <br>
    Total items: ${$length}
  </li>
</ul>

For nested repeats, you can access the outer scope with $parent:

<ul>
  <li repeat.for="item of items">
    Outer index: ${$parent.$index}
  </li>
</ul>

Working with Arrays

Arrays are the most common data source for repeats. Here’s an example component and its template:

Component (my-component.ts):

export class MyComponent {
  items = [
    { name: 'John' },
    { name: 'Bill' }
  ];
}

Template (my-component.html):

<ul>
  <li repeat.for="item of items">
    ${item.name}
  </li>
</ul>

Note: Aurelia tracks changes in arrays when you use array methods like push, pop, or splice. Direct assignments (e.g., array[index] = value) won’t trigger change detection.

Generating Ranges

repeat.for isn’t limited to collections—it can also generate a sequence of numbers. For instance, to create a countdown:

<p repeat.for="i of 10">
  ${10 - i}
</p>
<p>Blast Off!</p>

This iterates 10 times and computes 10 - i on each pass.

Iterating Sets

Sets are handled much like arrays. The syntax remains the same, though the underlying collection is a Set.

Component (repeater-template.ts):

export class RepeaterTemplate {
  friends: Set<string> = new Set(['Alice', 'Bob', 'Carol', 'Dana']);
}

Template (repeater-template.html):

<template>
  <p repeat.for="friend of friends">
    Hello, ${friend}!
  </p>
</template>

Iterating Maps

Maps offer a powerful way to iterate key-value pairs. Aurelia lets you deconstruct the map entry directly in the template.

Component (repeater-template.ts):

export class RepeaterTemplate {
  friends = new Map([
    ['Hello', { name: 'Alice' }],
    ['Hola', { name: 'Bob' }],
    ['Ni Hao', { name: 'Carol' }],
    ['Molo', { name: 'Dana' }]
  ]);
}

Template (repeater-template.html):

<p repeat.for="[greeting, friend] of friends">
  ${greeting}, ${friend.name}!
</p>

Here, [greeting, friend] splits each map entry so you can access both the key (greeting) and value (friend).

Iterating Objects via Value Converters

Objects aren’t directly iterable in Aurelia. To iterate over an object’s properties, convert it into an iterable format using a value converter.

Creating a Keys Value Converter

// resources/value-converters/keys.ts
export class KeysValueConverter {
  toView(obj: object): string[] {
    return Reflect.ownKeys(obj) as string[];
  }
}

Using the Converter in a Template

<import from="resources/value-converters/keys"></import>

<p repeat.for="key of friends | keys">
  ${key}, ${friends[key].name}!
</p>

The keys converter transforms the object into an array of its keys, making it iterable by repeat.for.

Custom Collection Handling with Repeat Handlers

Aurelia’s repeat system is extensible. If you need to iterate over non-standard collections (like HTMLCollections, NodeLists, or FileLists), you can create a custom repeat handler by implementing the IRepeatableHandler interface.

Custom Handler Example

import Aurelia, { Registration, IRepeatableHandler } from 'aurelia';

class ArrayLikeHandler implements IRepeatableHandler {
  static register(container) {
    Registration.singleton(IRepeatableHandler, ArrayLikeHandler).register(container);
  }

  handles(value: any): boolean {
    return 'length' in value && typeof value.length === 'number';
  }

  iterate(items: any, callback: (item: any, index: number) => void): void {
    for (let i = 0, len = items.length; i < len; i++) {
      callback(items[i], i);
    }
  }
}

Aurelia.register(
  ArrayLikeHandler,
  // other registrations
).app(MyApp).start();

Tip: Aurelia provides a default ArrayLikeHandler you can import directly:

import Aurelia, { ArrayLikeHandler } from 'aurelia';

Custom Handler Resolver

If you need to override the default order in which Aurelia selects a repeat handler, you can implement your own IRepeatableHandlerResolver:

class MyRepeatableHandlerResolver {
  resolve(value: any) {
    if (typeof value?.length === 'number') {
      return {
        iterate(items, callback) {
          for (let i = 0; i < items.length; ++i) {
            callback(items[i], i, items);
          }
        }
      };
    }
    throw new Error('The repeater supports only array-like objects.');
  }
}

This custom resolver can redefine how different collection types are handled by the repeater.

Summary

Aurelia 2’s list rendering capabilities are both powerful and flexible:

  • Versatile Iteration: Work with arrays, sets, maps, ranges, and even objects (via converters).

  • Efficient Updates: Use keyed iteration to minimize DOM changes.

  • Contextual Data: Access properties like $index, $first, $last, $even, $odd, $length, and $parent for richer templates.

  • Extensibility: Create custom handlers and resolvers to support any iterable data structure.

Mastering these features enables you to build dynamic, efficient UIs that handle complex data sets with ease.

PreviousConditional RenderingNextLambda Expressions

Last updated 3 months ago

Was this helpful?