Re: [WICG/webcomponents] [templates] A declarative JavaScript templating API (Issue #1069)

There was a question at the TPAC breakout about the process of turning a JS template expression into a `<template>` with DOM Parts.

The transform (a bit simplified) breaks down like this:

First, we have the `html` tag implementation which captures its arguments:

```ts
function html(strings, ...values) {
  return {kind: 'html', strings, values};
}
```
Then we have a function that returns a template expression:

```ts
const renderUser = (user) => html`<span>${user.firstName}, ${user.lastName}</span>`;
```

When called:

```ts
renderUser({firstName: 'John', lastName: 'Doe'});
```

it will return an object like:

```ts
{
  kind: 'html',
  strings: ['<span>', ', ', '</span>'],
  values: ['John', 'Doe'],
}
```

With the object we need to be able to either perform an initial render to a new place in the DOM, or update an existing fragment of DOM already created from it. We need a template that's able to be interpolated with any values, not just the values from the initial render.

So we need a template with parts where the values _would_ go, but that doesn't use the values. The only strings we have are `['<span>', ', ', '</span>']`. This array has one more item than the values array. The values will go in between the string spans we have, so to create the template HTML with placeholders for the values, we can join the strings with the DOM Parts marker (`{{}}`):

```ts
const templateHtml = templateResult.strings.join('{{}}');
```

which gives us:
```html
<span>{{}}, {{}}</span>
```

There's nothing inside the expression markers, like a name, because we don't have anything we _can_ put there. The `html` template tag doesn't get the text of the expressions in the template, only the evaluated values. We can't put those in the template because they're valid only for one particular render (and it's not safe to put them in the template HTML).

So then we can make a template:

```ts
const template = document.createElement('template');
template.setAttribute('parseparts', '');
template.innerHTML = templateHtml;
```

and to render it we clone it with parts, append the clone, and set the value of the parts.

```ts
const {node, parts}  = template.cloneWithParts();
container.append(node);
parts.forEach((part, i) => part.setValue(templateResult.values[i]));
```

I hope that explains why the DOM Part expressions are empty (`{{}}`). There is no name or identifier that we have to put there other than their index in the values array, which they already have implicitly by order.

And this is one reason why the JS API is simpler to do than the HTML syntax with expressions, because we don't need to specify any new expression language, or even identifier within the expression markers. The JS API doesn't need any of that to get a lot of utility from the basic DOM Parts delimiter syntax.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/1069#issuecomment-2375564526
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/1069/2375564526@github.com>

Received on Thursday, 26 September 2024 01:37:25 UTC