Skip to content

Latest commit

 

History

History
273 lines (197 loc) · 5.69 KB

custom-attributes.md

File metadata and controls

273 lines (197 loc) · 5.69 KB

Custom attributes

Custom attributes in Aurelia allow you to extend HTML elements with additional functionality. This guide covers various ways to create and use custom attributes.

Creating Custom Attributes

Convention-based Custom Attributes

Aurelia uses naming conventions to recognize custom attributes automatically.

export class UppercaseCustomAttribute {
  static inject = [Element];

  constructor(element) {
    this.element = element;
  }

  valueChanged(newValue) {
    this.element.textContent = newValue.toUpperCase();
  }
}

Usage:

<span uppercase.bind="name"></span>

Explicit Custom Attributes

Use the @customAttribute decorator for explicit declaration.

import {customAttribute, inject} from 'aurelia-framework';

@customAttribute('uppercase')
@inject(Element)
export class UppercaseCustomAttribute {
  constructor(element) {
    this.element = element;
  }

  valueChanged(newValue) {
    this.element.textContent = newValue.toUpperCase();
  }
}

Single Value Binding

By default, custom attributes use a single-value binding.

@customAttribute('font-size')
export class FontSizeCustomAttribute {
  valueChanged(newValue) {
    this.element.style.fontSize = newValue;
  }
}

Usage:

<div font-size.bind="fontSize">Resizable text</div>

Accessing the Element

Inject the Element to access the DOM element.

@customAttribute('tooltip')
@inject(Element)
export class TooltipCustomAttribute {
  constructor(element) {
    this.element = element;
  }

  valueChanged(newValue) {
    this.element.title = newValue;
  }
}

Multi-value Bindable Properties

Use @bindable for multiple properties.

import {customAttribute, bindable} from 'aurelia-framework';

@customAttribute('border')
export class BorderCustomAttribute {
  @bindable width = '1px';
  @bindable color = 'black';
  @bindable style = 'solid';

  bind() {
    this.element.style.border = `${this.width} ${this.style} ${this.color}`;
  }
}

Usage:

<div border="width: 2px; color: red; style: dashed;">Bordered content</div>

Responding to Bindable Property Changes

Implement *Changed methods to respond to property changes.

@customAttribute('highlight')
export class HighlightCustomAttribute {
  @bindable color = 'yellow';

  colorChanged(newValue, oldValue) {
    this.element.style.backgroundColor = newValue;
  }
}

Options Binding

Use options binding for complex configurations.

@customAttribute('popover')
export class PopoverCustomAttribute {
  @bindable({ defaultBindingMode: bindingMode.oneTime }) options = {};

  bind() {
    // Initialize popover with options
  }
}

Usage:

<button popover.bind="{ title: 'Info', content: 'Click for details' }">Info</button>

Specifying a Primary Property

Use primaryProperty for a default property.

import {customAttribute, bindable} from 'aurelia-framework';

@customAttribute('tooltip')
export class TooltipCustomAttribute {
  @bindable({ primaryProperty: true }) content = '';
  @bindable placement = 'top';

  bind() {
    // Initialize tooltip
  }
}

Usage:

<button tooltip="Click me!">Hover for tooltip</button>

Lifecycle Hooks

Implement lifecycle hooks for fine-grained control.

@customAttribute('fade-in')
export class FadeInCustomAttribute {
  attached() {
    this.element.style.opacity = '0';
    setTimeout(() => {
      this.element.style.transition = 'opacity 0.5s';
      this.element.style.opacity = '1';
    }, 0);
  }

  detached() {
    this.element.style.opacity = '';
    this.element.style.transition = '';
  }
}

Caveats and Considerations

Naming Conflicts

When using convention-based custom attributes, be cautious of naming conflicts with built-in HTML attributes or other libraries.

// This might conflict with the native 'style' attribute
export class StyleCustomAttribute { ... }

To avoid conflicts, consider using a prefix or the @customAttribute decorator with a unique name.

Unexpected Behavior with One-time Bindings

One-time bindings (.one-time) won't update the attribute if the bound value changes after initial binding.

<div my-attribute.one-time="dynamicValue"></div>

Use .bind or .to-view for values that might change.

Attribute vs. Element Semantics

Custom attributes modify existing elements, while custom elements create new elements. Be mindful of this distinction when deciding between the two.

<!-- Attribute: modifies existing element -->
<div my-attribute></div>

<!-- Element: creates new element -->
<my-element></my-element>

Case Sensitivity in Templates

Custom attribute names in Aurelia 1 are case-insensitive and follow a specific convention:

  • Define custom attribute classes using PascalCase, typically with a 'CustomAttribute' suffix.
  • Use kebab-case in HTML templates when applying the custom attribute.
  • Aurelia does not automatically convert between different cases.
// Definition: PascalCase
export class MyCustomAttributeCustomAttribute {
  // ...
}

Usage:

<!-- Usage: kebab-case -->
<div my-custom-attribute="value"></div>

Be aware that using camelCase or PascalCase in templates will not work:

<!-- These won't work -->
<div myCustomAttribute="value"></div>
<div MyCustomAttribute="value"></div>

Unbinding and Memory Leaks

Ensure proper cleanup in the unbind() lifecycle method to prevent memory leaks, especially when working with third-party libraries.

@customAttribute('third-party-plugin')
export class ThirdPartyPluginAttribute {
  unbind() {
    // Clean up third-party plugin
  }
}