# Attribute mapping

Attribute mapping is Aurelia's way of keeping template syntax concise. After an attribute pattern parses the attribute name but before a binding command emits instructions, the mapper answers two questions:

1. Which DOM property does this attribute target?
2. Should `.bind` implicitly behave like `.two-way` for this attribute?

This is the mechanism that lets you write `<input value.bind="message">` and automatically get a two-way binding. By teaching the mapper about your own elements, you can bring the same ergonomics to Web Components, design systems, or DSLs.

## When to extend `IAttrMapper`

Reach for the mapper when:

* **Bridging custom elements** – Third-party components often expose camelCase properties such as `valueAsDate` or `formNoValidate`.
* **Designing DSLs** – Attributes like `data-track` or `foo-bar` need to land on specific DOM properties regardless of casing.
* **Improving authoring ergonomics** – Upgrading `progress.bind` to two-way on slider-like controls keeps templates readable.

If you need to invent new attribute syntaxes (`[(value)]`, `@click`, etc.), start with [attribute patterns](https://docs.aurelia.io/components/template-compilation/attributepattern). If you need to observe DOM properties, follow up with the [node observer locator](https://docs.aurelia.io/components/extending-templating-syntax#combining-the-attribute-syntax-mapper-with-the-node-observer-locator).

## How the mapper decides

When Aurelia encounters an attribute that does not belong to a custom element bindable, it walks through the mapper logic:

1. Check tag-specific mappings registered via `useMapping`.
2. Fall back to global mappings from `useGlobalMapping`.
3. If no mapping exists, camelCase the attribute name.
4. If the binding command is `bind`, ask each predicate registered via `useTwoWay` whether the attribute should become two-way.

Your extensions only run for attributes that are not already handled by custom element bindables, so you can layer mappings without unintentionally overriding component contracts.

## Registering mappings during startup

Use `AppTask.creating` to register mappings before Aurelia instantiates the root component:

```typescript
import Aurelia, { AppTask, IAttrMapper } from 'aurelia';

Aurelia.register(
  AppTask.creating(IAttrMapper, attrMapper => {
    attrMapper.useMapping({
      'MY-CE': { 'fizz-buzz': 'FizzBuzz' },
      INPUT: { 'fizz-buzz': 'fizzbuzz' },
    });
    attrMapper.useGlobalMapping({
      'foo-bar': 'FooBar',
    });
  })
);
```

Keys inside `useMapping` must match the element's `tagName` (uppercase). The destination values must match the actual DOM property names exactly (`formNoValidate`, not `formnovalidate`).

With the mapping above in place, templates stay clean:

```html
<input fizz-buzz.bind="userLimit" foo-bar.bind="hint" ref="input">
<my-ce fizz-buzz.bind="42" foo-bar.bind="43" ref="myCe"></my-ce>
```

```typescript
export class App {
  private input!: HTMLInputElement;
  private myCe!: HTMLElement & { FizzBuzz?: number; FooBar?: number };

  public attached() {
    console.log(this.input.fizzbuzz); //  userLimit
    console.log(this.myCe.FizzBuzz);  //  42
  }
}
```

## Enabling implicit two-way bindings

Some controls should default to two-way binding even when authors write `.bind`. Use `useTwoWay` to register a predicate `(element, attrName) => boolean`:

```typescript
import Aurelia, { AppTask, IAttrMapper } from 'aurelia';

Aurelia.register(
  AppTask.creating(IAttrMapper, attrMapper => {
    attrMapper.useTwoWay((element, attrName) =>
      element.tagName === 'MY-CE' && attrName === 'fizz-buzz');
  })
);
```

Predicates receive the live element, so you can inspect classes, attributes, or even dataset values before opting into two-way. Keep the logic lightweight—these predicates run for every `*.bind` attribute Aurelia encounters.

## Troubleshooting and best practices

* **Uppercase tag names** – Browsers expose `element.tagName` in uppercase; use the same casing in `useMapping`.
* **Avoid duplicates** – Registering the same tag/attribute combination twice throws. Remove or consolidate old mappings before adding new ones.
* **Destination accuracy** – Mistyped destination properties silently fall back to camelCase conversion. Inspect the element in devtools and read `Object.keys(element)` if unsure.
* **Predicate order matters** – `useTwoWay` predicates run in registration order. Put the most specific check first.
* **Verify manually** – Toggle the DOM property in devtools. If the UI updates but Aurelia does not, revisit the observer configuration. If neither updates, revisit the mapping.
* **Pair with observers** – Mapping alone does not teach Aurelia how to observe custom properties. Follow up with `INodeObserverLocator.useConfig` so bindings know which events to listen to.

With the mapper tailored to your components, you can keep templates expressive while relying on the full power of Aurelia's binding system.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aurelia.io/components/template-compilation/attributemapper.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
