Fold: reach inherited @aware values through the data stack - #200
Fold: reach inherited @aware values through the data stack#200oneleggedswede wants to merge 1 commit into
Conversation
Folding a component that declares @AWare merges the inherited value into that component's own attribute bag. At render time the value never goes there -- it reaches the component through the data stack, and the bag holds only what the call site wrote -- so the two pipelines disagree for any component that asks where a value came from rather than what it is. The case that surfaced it is a form control inheriting its field name from a wrapper. Unfolded, its bag has no `name`, so it can tell it is one of a group; folded, `name` is merged in and it reads as a control that named itself, and draws the group's validation message a second time under every box. A wire:model control picks up a `name` its binding meant to replace the same way. Neither shows up as broken markup -- it is well formed, it just says something else. mergeAwareProps() already knows the answer: it merges only where the call site did not write the key. This keeps those values in a frame of their own and pushes it under the component's, so @AWare resolves in the order it does at render -- own first, then ancestors -- while the bag stays clean. The two comparison tests render the same template through both pipelines. The first fails without this change (`data-source="own"` against `data-source="inherited"`); the second covers the other direction, so a value genuinely written on the tag is still read as the component's own.
Benchmark Result: Default
Median of 10 attempts (* = outlier, excluded from result), 5000 iterations x 10 rounds, 47.84s total To run a specific benchmark, comment |
|
Hi @oneleggedswede, thanks for contributing and reporting the issue! I have addressed this in #201, fixing the issue more broadly and adding few focused tests. I'll close this PR but I’ve added you as a co-author to the other one. In your example and test, you capture the attribute bag before When the above fix is released you can remove this workaround as the attribute bag will remain untouched. |
Folding a component that declares
@awaremerges the inherited value into that component's own attribute bag. At render time the value never goes there — it reaches the component through the data stack, and the bag holds only what the call site wrote.For most components that difference is invisible: both routes hand back the same value. It matters for a component that asks where a value came from rather than what it is.
The case that surfaced it
A form control that inherits its field name from a wrapper, and uses "was this written on my tag?" to decide whether it owns its own validation message:
Unfolded, the checkbox's bag has no
name, so it can tell it is one of a group and leaves the message to the field. Folded,name="tags"is merged into its bag, it reads as a control that named itself, and the group's message is drawn again under every box. Awire:modelcontrol picks up anameattribute its binding meant to replace in the same way.Neither shows up as broken markup — it is well formed, it just says something else.
@awareunsetting the key afterwards doesn't help a component that has to snapshot and restore the bag around the directive, which is what a control does when the value also has to reach the rendered element.The change
mergeAwareProps()already knows the answer — it merges only where the call site did not write the key:…and then discards it. This keeps those values in a frame of their own and pushes it under the component's, so
@awareresolves in the order it does at render — own first, then ancestors — while the bag stays clean.Tests
Two cases added to
ComparisonTest, which renders the same template through both pipelines and asserts byte equality.The first fails on
mainand passes here:The second covers the other direction, so a value genuinely written on the tag is still read as the component's own. The existing suite is unchanged: 266 passing before, 268 after.
Note
InvalidBlazeFoldUsageException::forAware()is defined and never called. With this,@awareandfoldcompose rather than being something to warn about — happy to wire the exception up instead if you'd rather go the other way, but this seemed the better end of it.