Modifying template parsing with AttributePattern

Aurelia's attribute pattern system allows you to create custom template syntax extensions that can emulate other framework syntaxes like Angular or Vue, or define entirely new patterns for your specific needs. This powerful extensibility feature integrates directly with Aurelia's template compiler and binding engine.

Architecture Overview

The attribute pattern system consists of several core components:

  • AttributePatternDefinition: Defines pattern structure with pattern and symbols

  • AttrSyntax: The parsed result containing binding information

  • SyntaxInterpreter: A finite state machine that efficiently parses attribute names

  • AttributeParser: Manages pattern registration and result caching

  • Pattern Priority System: Resolves conflicts when multiple patterns match

When to reach for attribute patterns

Create an attribute pattern when the attribute name itself needs to convey extra meaning. Typical use cases include:

  • Porting syntaxes from other frameworks ([(value)], @click, :value, #ref).

  • Building DSLs where symbols separate intent (for example, data-track.click.once).

  • Collapsing multiple instructions into one attribute, such as emit:save or listen:customer.updated.

If you simply need value.bind to default to two-way binding, prefer the attribute mapper. If you want to change how an attribute behaves after it has been parsed, reach for a binding command instead. Attribute patterns run before the mapper and binding commands, so they are ideal for inventing new syntaxes.

Basic Pattern Definition

AttributePatternDefinition Interface

The PART Keyword

PART in patterns represents dynamic segments that can match any characters except those defined in symbols. Think of PART as a flexible placeholder equivalent to the regex ([^symbols]+).

Symbols Behavior

The symbols property defines characters that:

  • Act as separators between pattern segments

  • Are excluded from PART matching

  • Can be used for readability and structure

Example:

  • foo@bar → parts: ['foo', 'bar'] (with symbols)

  • Without symbols → parts: ['foo@', 'bar'] (without symbols)

Pattern Class Structure

Basic Pattern Class

Note: AttrSyntax must be imported from @aurelia/template-compiler, not from the main aurelia package, as it's not currently re-exported there.

Pattern Method Signature

Each pattern method must:

  1. Have the exact same name as the pattern string

  2. Accept three required parameters:

    • rawName: string - Original attribute name (e.g., "[(value)]")

    • rawValue: string - Attribute value (e.g., "message")

    • parts: readonly string[] - Extracted PART values (e.g., ["value"])

  3. Return an AttrSyntax instance

AttrSyntax Constructor

The AttrSyntax class has the following constructor signature:

AttrSyntax Parameters Explained

Parameter
Description
Example

rawName

Original attribute name from template

"[(value)]"

rawValue

Original attribute value

"message"

target

The target property, element, or identifier

"value"

command

Binding command type

"two-way", "bind", "trigger", "ref"

parts

Additional parts for complex patterns

For event modifiers, extended syntax

Common Binding Commands

  • 'bind' - One-way to view binding

  • 'to-view' - Explicit one-way to view

  • 'from-view' - One-way from view

  • 'two-way' - Two-way data binding

  • 'trigger' - Event binding

  • 'capture' - Event capture

  • 'ref' - Element/component reference

  • null - Custom or no specific command

Pattern Registration

Global Registration

Register patterns globally at application startup:

Local Registration

Register patterns for specific components:

Inline Pattern Definition

For simple patterns, you can define them inline:

Multiple Patterns per Class

A single class can handle multiple related patterns:

Pattern Priority System

When multiple patterns could match the same attribute name, Aurelia uses a priority system:

  1. Static segments (exact text matches) have highest priority

  2. Dynamic segments (PART) have medium priority

  3. Symbol segments have lower priority

Example Priority Resolution:

Advanced Pattern Examples

Event Modifiers

Static Patterns (No PART)

Complex Multi-PART Patterns

Built-in Pattern Examples

Aurelia includes several built-in patterns you can reference:

Dot-Separated Patterns

Shorthand Binding Patterns

Framework Syntax Examples

Angular-Style Patterns

Vue-Style Patterns

Performance Considerations

Caching System

The attribute parser maintains an internal cache of parsed interpretations. Once an attribute name is parsed, the result is cached for subsequent uses, improving template compilation performance.

Pattern Optimization

  • Order Matters: More specific patterns should be defined first when possible

  • Symbol Selection: Choose symbols that don't conflict with common attribute patterns

  • Minimal Patterns: Avoid overly complex patterns that could match unintended attributes

Registration Timing

Patterns must be registered before template compilation begins. Late registration after the application starts may not take effect for already-compiled templates.

Debugging and Error Handling

Common Pattern Errors

  1. Missing Method: Pattern method name doesn't match pattern string exactly

  2. Wrong Signature: Method signature doesn't match required parameters

  3. Symbol Conflicts: Pattern symbols conflict with other registered patterns

  4. Registration Timing: Patterns registered after compilation begins

Debugging Tips

Pattern Testing

Test your patterns with various attribute combinations:

Integration with Template Compiler

Attribute patterns integrate seamlessly with Aurelia's template compilation process:

  1. Template Analysis: The compiler scans for all attributes

  2. Pattern Matching: Each attribute name is tested against registered patterns

  3. Syntax Creation: Matching patterns create AttrSyntax objects

  4. Binding Generation: The compiler generates appropriate bindings based on the syntax

  5. Runtime Execution: Bindings execute during component lifecycle

Working alongside binding commands and the attribute mapper

  • Attribute patterns decide the final target and command for an attribute. They are the only hook that can rewrite foo.bar.baz into whatever structure you need.

  • Binding commands use that parsed information to produce instructions. If your pattern returns command: 'permission', the binding command named permission will receive the attribute.

  • Attribute mapper only runs when command === 'bind'. If your pattern emits 'bind', the mapper can still remap value.bind to value.two-way or translate attribute names into DOM properties.

Design patterns so they hand off clear targets and commands to the downstream pipeline. When in doubt, log the resulting AttrSyntax objects while authoring your pattern to confirm the values that later hooks will see.

Best Practices

Pattern Design

  1. Intuitive Syntax: Create patterns that feel natural to developers

  2. Consistent Naming: Follow consistent conventions across related patterns

  3. Clear Symbols: Use symbols that clearly separate pattern parts

  4. Avoid Conflicts: Test patterns against existing Aurelia syntax

Registration Strategy

  1. Global vs Local: Use global registration for widely-used patterns, local for component-specific ones

  2. Bundle Size: Consider the impact of registering many patterns globally

  3. Tree Shaking: Local registration helps with tree shaking unused patterns

Error Recovery

  1. Graceful Fallback: Design patterns to fail gracefully when they don't match

  2. Clear Errors: Provide meaningful error messages in pattern methods

  3. Validation: Validate pattern inputs and provide helpful feedback

Complete Examples

Custom Framework Integration

Advanced Component System

The attribute pattern system provides unlimited flexibility for creating custom template syntaxes that fit your team's needs or emulate familiar patterns from other frameworks, all while maintaining full integration with Aurelia's binding and compilation systems.

Quick Reference Cheatsheet

Here's a corrected cheatsheet with working examples:

Next steps

  • Pair attribute patterns with the attribute mapper when you need to translate new syntaxes into existing DOM APIs.

  • Continue with Extending templating syntax to see how patterns, mappers, and observers work together end-to-end.

  • Explore custom binding commands whenever your pattern should hand off to bespoke runtime behavior instead of the default bind/two-way commands.

Last updated

Was this helpful?