# Directives The `e:`-prefixed attributes: `e:for`, `e:if` / `e:elseif` / `e:else`, `e:switch` / `e:case` / `e:default`, and `e:literal`. 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. ```ehtml
  • {user.name}
  • {index}: {user.name}
  • {key}: {lookup[key]}
  • ``` **Filtering and shaping.** The right-hand side is an expression, so filter, sort, or reshape the rows there. The loop re-evaluates the expression when anything it read changes, and reconciles the new row set against the old one by key, so a row present in both keeps its dom. ```ehtml
  • matches(t, view.filter))}> {todo.title}
  • ``` A helper works the same way. A function that builds its own array from the rows, `feed(messages)` say, is live because the loop's evaluation runs the helper, and the helper iterated the view. Whatever the expression reads on the way, a tab, a search string, the view itself, becomes something the loop follows. `LiveView` supports `filter`, `sort`, `map`, `find`, `at`, `length`, and the rest of the non-mutating Array surface directly, and each returns a plain array. `todos.filter(...)` is the whole answer for a list already on screen; nothing is re-fetched. (`recipes/search-filter` is the other case: a server-side re-query for a result set too large to hold in the browser.) Group with a flag on the row, not a loop inside a loop. Keep one flat loop and compute the grouping fact for each row as you build the list: ```ehtml
  • line.id}>

    {line.dayLabel}

    {line.message.body}
  • ``` `Map.groupBy` with a nested loop renders the same page and repaints all of it. A `[key, rows]` entry holds a fresh inner array every time the expression is evaluated, so the group compares unequal to the one before it, takes the update path, and re-renders itself and every row nested inside. One arriving row rebuilds its whole group, and with an animation on a row that is a visible flicker. `recipes/grouped-feed` has the working shape, the helper that builds the lines, and the measurement. `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`: ```ehtml
  • 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 ```ehtml
    loading
    error: {error.message}
    ready
    ``` Branches must be siblings. Whitespace between them is allowed. A directive applies to one element. To put several siblings under one condition, wrap them in an element. ```ehtml
    ``` `