# Directives
The `e:`-prefixed attributes: `e:for`, `e:if` / `e:elseif` / `e:else`, and
`e:switch` / `e:case` / `e:default`.
Directives are `e:`-prefixed attributes. They compose with normal attributes on
the same element. On an element with both `e:if` and `e:for`, the `e:if` is
evaluated first: a false `e:if` means the element does not render at all, and a
true `e:if` allows `e:for` to iterate. It is all-or-nothing for the whole loop.
**`e:if` cannot filter individual rows.** It decides whether the loop runs, not
which items it runs over. To show a subset, filter the iterable, as below. Do
not render every row and hide some with a class or `display: none`: the rows
are still there, in the DOM and in the accessibility tree, and counts taken
from the list will be wrong.
### e:for
Iterates over any iterable. The result is reactive: updates patch the DOM in
place at the row level.
```html
{user.name}
{index}: {user.name}
{key}: {lookup[key]}
```
**Filtering.** The right-hand side is an expression, so filter it there. The
result stays reactive both ways: it re-renders when the filter changes, and it
re-renders when the underlying data changes, including a `LiveTable` row
arriving from another browser while a filter is applied.
```html
matches(t, view.filter))}>
{todo.title}
```
**Keep the `filter(...)` in the loop's expression.** Hoisting it into a helper
looks equivalent and is not:
```html
```
The loop re-runs when something it read changes. The inline predicate reads
`view.filter` each time the rows are walked, so the loop follows it. As a
helper argument, `view.filter` is read once while the argument is evaluated and
the loop never learns the tabs can change it. The tab's own highlight still
updates, so it looks like the filter ran and only the rows are wrong.
`LiveTable` supports `find` / `filter` / `sort` / `map` / `at` / `length`
directly, so `todos.filter(...)` is the whole answer for a list already on
screen. Nothing is re-fetched. `filter` and `sort` return a live view rather
than an array, so a row arriving over the wire is placed with one dom
insertion instead of re-rendering the list. (`elements man recipes/search-filter` is the other case: a
server-side re-query for a result set too large to hold in the browser.)
`for...of` and `for...in` work the same as in JavaScript: `of` iterates arrays
and other iterables; `in` iterates object keys, string characters, and similar.
The Elements runtime patches `for...of` at the row level. `for...in` re-renders
the whole list when the right side changes.
By default rows are keyed by the `id` field on each item. Provide an `id` and
the runtime diffs by it, patching only the rows that changed. Without an `id`,
rows key by object identity, so replacing the array with fresh objects (say, a
refetch) re-renders every row.
Use `e:key` to supply your own key function when a row has no `id`, or when the
stable key isn't a field on the row, such as iterating `Object.entries`,
composite keys, and so on. It takes the iteration value and returns a `string` or
`number`:
```html
item.id}>
{index} - {item.description}
```
The key function's parameter is the `e:for` iteration value, typed the same as
the loop binding, so a wrong destructure or a missing field is a compile error.
`e:key` pairs with `e:for` on the same element (either order).
### e:if / e:elseif / e:else
```html
loading
error: {error.message}
ready
```
Branches must be siblings. Whitespace between them is allowed.
### e:switch / e:case / e:default
Parent holds the expression, children are cases.
```html