- From: Justin Fagnani <notifications@github.com>
- Date: Sun, 16 Jul 2023 11:06:54 -0700
- To: WICG/webcomponents <webcomponents@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <WICG/webcomponents/issues/1011/1637153738@github.com>
We've gone through a couple of these APIs in lit-html, which has nearly an identical concept to AttributePart, and from that I think there are a few considerations to judge the options by: ### 1. Syntax and expression-to-part association Given a syntax that creates attribute parts, is there a 1-1 association between expressions and parts or not? ie, given a template like: ```html <template> <x-foo fullname="{{ lastName }}, {{ firstName}}"> </template> ``` Does this create one AttributePart or two? The argument in favor of a 1-1 association is that updating entire template instances becomes easy - you typically have an array of values, and with an equal length and ordered array of parts, you just do: ```ts parts.forEach((part, i) => part.setValue(values[i])); ``` This consideration still applies without a native syntax, because userland systems will have a syntax and often a similar list of values to assign to parts. ### 2. API for setting all attribute partial values at once Is there an API, either specific to a group of AttributeParts, or more general for a whole template instance, for setting all the values for an attribute at once. This might be a multi-valued AttributePart, like Option 3: ```ts part.setValue([firstName, lastName]); ``` An attribute group, like Option 2: ```ts group.setValues([firstName, lastName]); Or a more generic PartGroup: ```ts // This might contain attribute and other parts partGroup.update([x, firstName, lastName, y]); ``` Another option is that AttributeParts are updated individually, but all parts have to be committed to update the DOM, and AttributeParts that share an attribute also share a dirty/committed state such that you can update them at once: ```ts firstNamePart.setValue(firstName); lastNamePart.setValue(lastName); firstNamePart.commit(); // sets the attribute lastNamePart.commit(); // no-op: the attribute is already committed ``` This allows generic handling of all part types, but requires two loops to do so: ```ts function update(values) { parts.forEach((part, i) => parts.setValue(values[i])); parts.forEach((part) => part.commit()); } ``` ### 3. API for setting individual partial values The API for setting just one partial comes up when you have any extension or update system that operates at the individual expression level, such as lit-html's directives or many other libraries Signals systems. One question is whether those updaters can operate on a general `Part` interface, or whether they have to special-case multi-valued parts like Attribute Part. Another question is how individual updates interact with batching. Sometimes an update comes in as part of an external batch. lit-html updates all parts during a render in a batch, so attribute expressions wrapped in a directive shouldn't cause the attribute to commit early before the batch, but an update outside of one of those batches should update immediately. Signal systems often have batched updates as well. The problem is a bit constrained. We would like: * The update API to be generic across all part types * Batching for the attribute when in a template update batch * No batching when updated outside of a batch The set/commit approach works, but puts an awkward requirement on extensions/directives that to update a part they have to call two methods: ```ts // part here represents an individual expression of possibly many for a single attribute part.setValue(value); part.commit(); ``` One way around this is a second argument to `setValue()` when batching: ```ts part1.setValue(value); // commits to DOM immediately part2.setValue(value, false); // requires a later commit part2.commit(); ``` If AttributeParts are multi-valued, there there is no API for setting an individual partial, and an extension would have to specially handle attribute parts, and know the index of the partial they're updating: ```ts updateAttributeExpression(part, value, index) { const currentValues = part.values; const newValues = [...currentValues.slice(0, index), value, ...currentValues.slice(index + 1) part.setValues(newValues); } ``` ---- fwiw, in lit-html we started with a two-pass set/commit system, but ended up with a multi-valued AttributePart due to a slight performance improvement. I don't like the ergonomics of either of those APIs myself, and would prefer one where there's a 1-1 expression-to-part association and a simple `setValue(value)` API for the non-batching case. I think a second argument to `setValue()` to defer when in a batch is maybe the nicer API. -- Reply to this email directly or view it on GitHub: https://github.com/WICG/webcomponents/issues/1011#issuecomment-1637153738 You are receiving this because you are subscribed to this thread. Message ID: <WICG/webcomponents/issues/1011/1637153738@github.com>
Received on Sunday, 16 July 2023 18:07:01 UTC