- From: Justin Fagnani <notifications@github.com>
- Date: Wed, 21 Aug 2024 09:19:16 -0700
- To: WICG/webcomponents <webcomponents@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <WICG/webcomponents/issues/1069/2302481648@github.com>
@mdesantis > 1. How would this integrate with events? Underlying all template bindings will be Parts from the DOM Parts proposal. We will need a syntax for attribute-like bindings that distinguishes between attribute, property, and event parts. That syntax should be part of the static strings of the template so that one part can be created and use for that binding for the lifetime of the template instance. So... some kind of prefix or sigil is mostly likely needed. In lit-html we use `.`, `@`, and `?` prefixes to denote property, event, and boolean attribute bindings. An event binding looks like: ```ts HTMLElement.html` <button @click=${(e) => { console.log('click'); }> Click me </button> ` ``` Now, I don't want to presume or propose a lit-html-like syntax at this moment, and I'm not sure if there'd be consensus on diverging from plain HTML like this, but there are some reasons why we chose these prefixes: 1. `@` is a pretty common prefix for events (used in Vue and others), while `.` looks like property access. 2. `.`, `@`, and `?` prefixes are parsable by the HTML parser, making it easy to generate template HTML from the template strings. This might not be a concern for a native implementation. 3. `.`, `@`, and `?` are _not_ valid attribute names for `setAttribute()`, making them less likely to be used in any existing HTML, so collisions with real attribute names are unlikely. I think the ergonomics work really well, and this setup has been copied by other libraries, so it seems like a good choice so far. Any standards proposal should consider all the options though. > Would something like this work? > > ```js > const count = new Signal.State(0); > > document.body.render(HTMLElement.html` > <div> > <p>You clicked ${count} times</p> > <button onClick=${() => count.set(count.get() + 1)}> > Click me > </button> > </div> > `); > ``` The problem with the React approach of just using property names for events are that 1) not every element has an event-handler property for every event that you might want to listen to. Bubbling and capturing really dispel that notion. 2) There can be collisions with non-event properties that happen to start with `on`. And 3) event-handler properties do not accept event options, while `addEventListener()` does and an EventPart should. I think an explicit syntax is much, much better. > 2. How would conditionals look? > 3. How would loops look? Conditionals and loops are one of the big reasons to do a template system in JavaScript before doing one in HTML, IMO. They just fall out naturally from a few things: 1. Template as expressions returning values 2. Composition of templates 3. Support for iterables 4. Relying on JavaScript for expressions and control flow With composition and template-as-values, you get conditionals: ```ts const {html} = HTMLElement; html` <div> ${user.isLoggedIn ? html`<p>Welcome, ${user.name}</p>` : html`<p>Please log in</p>` } </div> `; ``` Support for iterables gives you looping: ```ts const {html} = HTMLElement; html` <ul> ${items.map((item) => html`<li>${item.name}</li>`)} </ul> `; ``` Template-as-values also means that you can use any conditional or looping constructs that you want. You can use for-loops and push template results into an array. You can make custom conditional and iteration helpers. You can use generators, Sets, Maps, and any other iterable implementation. Keying and moving DOM when data is reordered and mutated is an important concern. This can be built in userland on top of stateful directives. A directive can store the previously rendered items, perform a list-diff on update, and manually move DOM around to preserve state as needed. To the use this would look like: ```ts import {keyedRepeat} from 'dom-template-utils'; const {html} = HTMLElement; html` <ul> ${keyedRepeat(items, (item) => item.id, (item) => html`<li>${item.name}</li>`)} </ul> `; ``` Where the second argument to `keyedRepeat` is a key function. -- Reply to this email directly or view it on GitHub: https://github.com/WICG/webcomponents/issues/1069#issuecomment-2302481648 You are receiving this because you are subscribed to this thread. Message ID: <WICG/webcomponents/issues/1069/2302481648@github.com>
Received on Wednesday, 21 August 2024 16:19:20 UTC