Skip to content

fix(fast-html): bind event handlers to correct host inside f-repeat#7331

Open
mohamedmansour wants to merge 1 commit intomicrosoft:mainfrom
mohamedmansour:fix/events-in-repeats
Open

fix(fast-html): bind event handlers to correct host inside f-repeat#7331
mohamedmansour wants to merge 1 commit intomicrosoft:mainfrom
mohamedmansour:fix/events-in-repeats

Conversation

@mohamedmansour
Copy link
Contributor

@mohamedmansour mohamedmansour commented Mar 20, 2026

Summary

Event handlers inside <f-repeat> now have their this context correctly bound to the host element instead of the repeat item.

Changes

  • utilities.ts: Extracted shared path-building logic from pathResolver into buildResolvedPath helper. Added eventOwnerResolver that resolves the owner of a method by walking the same context chain but stopping one segment before the method name (e.g. c.parent at nesting level 1).
  • template.ts: Changed .bind(x) to .bind(ownerResolver(x, c)) in the @ event binding branch, using eventOwnerResolver to determine the correct this target.

What still works

  • Level 0 (no repeat): eventOwnerResolver returns x (the host itself), so behavior is identical to before.
  • Self-referencing paths: Paths like item.doSomething that start with the repeat context variable correctly resolve the owner to the repeat item (x), not the host.
  • Nested repeats: The context-walking logic respects nesting depth, traversing parentContext/parent chains as needed.
  • All existing tests pass (365 Chromium + WebKit tests).

Fixes #7329.

## Summary

Event handlers inside `<f-repeat>` now have their `this` context
correctly bound to the host element instead of the repeat item.
Fixes microsoft#7329.

## Changes

- **`utilities.ts`**: Extracted shared path-building logic from
  `pathResolver` into `buildResolvedPath` helper. Added
  `eventOwnerResolver` that resolves the *owner* of a method by
  walking the same context chain but stopping one segment before
  the method name (e.g. `c.parent` at nesting level 1).
- **`template.ts`**: Changed `.bind(x)` to `.bind(ownerResolver(x, c))`
  in the `@` event binding branch, using `eventOwnerResolver` to
  determine the correct `this` target.

## What still works

- **Level 0 (no repeat)**: `eventOwnerResolver` returns `x` (the host
  itself), so behavior is identical to before.
- **Self-referencing paths**: Paths like `item.doSomething` that start
  with the repeat context variable correctly resolve the owner to the
  repeat item (`x`), not the host.
- **Nested repeats**: The context-walking logic respects nesting depth,
  traversing `parentContext`/`parent` chains as needed.
- **All existing tests pass** (365 Chromium + WebKit tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohamedmansour mohamedmansour force-pushed the fix/events-in-repeats branch from 8e9458c to 11db239 Compare March 20, 2026 10:04
Copy link
Collaborator

@radium-v radium-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a repeat directive, the template (second arg) expressions are bound to the result of the data binding result (first arg):

type Item = string;
type Items = Item[];

class MyElement extends FASTElement {
    public items: Items = ["one", "two", "three"];

    public someValue: string = "some value";

    public itemClickHandler(event: Event) {
        console.log("item clicked", event);
    }
}

export const template = html<MyElement>`
    ${repeat(
        x => x.items, // <-- the data binding object (aka `x` for bindings within the repeat's template)
        html<Item, MyElement>`
            <!-- the template data binding is `Item`, the template binding context is `MyElement` -->
            <button
                @click="${(currentItem: Item, thisBindingContext) =>
                    thisBindingContext.parent.itemClickHandler(thisBindingContext.event)}"
            >
                ${
                    // bindingContext allows access to the parent in any binding, anywhere in template expressions
                    (currentItem, bindingContext) => bindingContext.parent.someValue
                }
            </button>
        `
    )}
`;

In FAST HTML, we need to add a way to access the template binding context from within the repeat template, instead of re-contextualizing all bindings within an f-repeat. Providing access to the context object in any binding expression in declarative templates would be the right way to approach this problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FAST-HTML should support event handler context access inside f-repeat

2 participants