[w3c/webcomponents] [templates] Template proposal essentials (shave the mustache) (#739)

## Do we really need the mustache syntax?

Having a template string in JavaScript allow us to inject values into specific parts of a string:
```js
`word1 ${executedValue1} word2 ${executedValue2} ...`
```
It appears that the essence of html templates are very similar.

A mustache syntax is powerful, yet it is not descriptive enough (people already mentioned that in other issues e.g. #704). So the question is, should we have a template syntax at all? Taking into account that we can use function results in mustache templates as well as nested templates, means that we can avoid using any template syntax and just have a placeholder like we do in template string (the logic can be moved inside a function)
```html
<div>
  <h1>Hello {{name}}</h1>
  {{^name}} Wellcome! {{/name}}
<div>
```

Which can be rewritten:
```html
<div>
  <h1>Hello $$</h1>
  $$
<div>
```

The placeholder (`$$`) can be anything, but the point is that we don't have any logic in the template at all, and we can transform this template (string) into something very familiar:
```js
const strings = text.split('$$'); // Place your favourite separator here
// ['<div>/n  <h1>Hello ', '</h1>/n  ', '/n</div>']
```

The result is exactly the same as in tagged template string. It is native, performant, straightforward and we don't need anything new (no new syntax, no parser, nor anything else). But now, there are some parts that should be improved, i.e. we need to turn this array of strings into a DocumentFragment and an object that holds the array of context for the dynamic parts (values).

DocumentFragment representation:
```html
<div>
  <h1></h1>
<div>
```

and
```js
Parts = {
  update(instance, ...values) {
    this.context.forEach((context, index) => context.update(instance, values(index)));
  },
  context: [nodeContext1, nodeContext2]
}
```
Where `nodeContext1` and `nodeContext2` are placeholders that hold the values and have an update method that updates an appropriate part of the DocumentFragment.
```
nodeContext1 = {
   type: 'node', // There should be different types of context: node, slot, attribute, class, style and may be more
   value: '',
   update(instance, value) {
     this.dispatchEvent(new CustomEvent('contextupdate', {
       detail: {
         type,
         oldValue: this.value,
         newValue: value,
         part: this
       }
     })); // Update context callback
     this.value = value;
     instance.querySelector('h1').innerHTML = this.value; // it should be something more reliable based on a placeholder position in the array
  }
}
```

Having a regular DocumentFragment and Parts object, it is possible to update Fragment with a new data:
```js
Parts.update(instance, ...values); // update DOM instance with values
```

Moreover, we can add complex logic inside javascript template strings like below:
```
const instance = html`
  <div>
    <h1>${list.length ? list.length : ''}</h1>
  </div>
`;
```

Libraries like `lit-html` uses similar mechanism under the hood as well.

This way, we do not introduce any new syntax and solve the basic issue that can fit both html templates and template strings. Template logic can go to the template strings rather than the template itself (instead of {{#loop}} we can use native js). Having this bare minimum native implementation of templates, allows us to extend the power of templates with 3rd party libraries later.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/739

Received on Tuesday, 20 February 2018 17:22:50 UTC