w3c / webcomponents-cg

Web Components community group
https://w3c.github.io/webcomponents-cg/
189 stars 11 forks source link

Standards Advancement: HTML Template Instantiation #2

Closed ConradSollitt closed 3 years ago

ConradSollitt commented 3 years ago

Greetings Web Components Community Group!

I would like to start an informal topic on the possibility of having native browser support a templating language, some widely used templating language examples:

I think there are a large number of applications and sites that are too complex for basic <template> with <slot> but do not need the full power of Virtual DOM or a large Frameworks and instead could be developed using simple templating if it were allowed in the browser without HTML. For example a simple reporting site where data is downloaded from a web service and needs to be displayed with custom formatting, or really any site that needs to download and display data from a JSON service.

There was in fact a proposal in 2017 from Apple related to this, but it doesn't appear to be trending in any sort of tech news so I'm not sure what the status is:

For my brief background I've been developing websites with JavaScript for a little over 20 years with Web Components for probably about a year and a half now. Basically I've worked with a wide range of JavaScript libraries, frameworks, and server side tech over the years. I am now using Web Components for Production facing SPA's at work and I'm happy to report its working out very well. No build process just simple <script> to include the components. I found at least one major challenge needed to make them work properly was the need for basic templating.

To provide a brief example, here is a snippet from a demo app showing geographic data I had developed. Originally I had developed a <json-data> component where I passed data to a <data-table> component using a custom data-bind attribute. The custom labels and fields attributes specify what fields would display on screen. However to make the demo work properly (add links, formatting, icons) I resorted to JavaScript that ran after the table was rendered. At that point I had felt I was better off using non Web Components because I was using more JavaScript on the Web Component sites.

<json-data url="https...">
    <data-table
        data-bind="countries"
        labels="Code, Name, Size (KM), Population, Continent"
        fields="iso, country, area_km, population, continent">
    </data-table>
</json-data>

<!-- Lots of JavaScript needed for formatting -->
<script>
// ...
for (const row of table.tBodies[0].rows) {
    const iso = (pageIso ? pageIso : row.cells[0].textContent.trim().toLowerCase());
    const flag = document.createElement('i');
    flag.className = iso + ' flag';
    row.cells[1].insertAdjacentElement('afterbegin', flag);
    // ...
}
// ...
</script>

Later I had updated it to use JavaScript Template Literals (Template String) in replace of a templating engine and that worked well because then it made it fast enough for me to customize the result just like if I were using templating or JSX. Example:

<data-table
    data-bind="countries"
    labels="Code, Name, Size (KM), Population, Continent">
    <template>
        <tr>
            <td><a href="#/regions/${iso}">${iso}</a></td>
            <td>
                <i class="${iso.toLowerCase()} flag"></i>
                ${country}
            </td>
            <td class="align-right" data-value="${area_km}">${format.number(area_km)}</td>
            <td class="align-right" data-value="${population}">${format.number(population)}</td>
            <td>${continent}</td>
        </tr>
    <template>
</data-table>
<!-- No JavaScript Needed -->

That works for simple templates, but if resulting layout needs some logic the templates become much more complex and the result looks somewhat like JSX. While this is fine for advanced developers or developers familiar with React/JSX I feel using template literals is not easy enough for wider use (example power users at work who can create web content but are not programmers); plus development is not as fast compared to using a templating language with supported logic (Handlebars, Liquid, etc).

<template id="image">
    <li>
        <img src="${escapeHtml(image.src)}" />
        ${image.isUploading === true ? '<div class="loading">' + i18nText('Uploading...') + '</div>' : ''}
        ${image.hasError === true ? '<div class="error">' + i18nText('Error') + '</div>' : ''}
        ${image.predictions.map(function(prediction) {
            return '<div class="' + resultClass(prediction.probability) + '">' + prediction.label + ' = <span>' + format.percent(prediction.probability, 5) + '</span></div>'
        }).join('')}
    </li>
</template>

The above snippet is from a demo page I made where I wrote the same app with different client side technologies. Below are Vue and Handlebars versions of the exact same code. In my opinion of these 3 the most readable is the Vue version. Although if browsers could support native templating I would prefer it to be more like Mustache/Handlebars/Liquid.

<!-- Vue -->
<li v-for="image in images">
    <img v-bind:src="image.src" />
    <div class="loading" v-if="image.isUploading" v-i18n="'Uploading...'"></div>
    <div class="error" v-if="image.hasError" v-i18n="'Error'"></div>
    <div v-for="prediction in image.predictions" v-bind:class="resultClass(prediction.probability)">
        {{ prediction.label }} = <span v-format-percent:5="prediction.probability"></span>
    </div>
</li>

<!-- Handlebars -->
{{#each images}}
<li>
    <img src="{{src}}" />
    {{#if isUploading}}
        <div class="loading">{{i18n 'Uploading...'}}</div>
    {{/if}}
    {{#each predictions}}
        <div class="{{js (concat 'model.resultClass(' this.probability ')')}}">
            {{this.label}} = {{formatPercent this.probability 5}}
        </div>
    {{/each}}
    {{#if hasError}}
        <div class="error">{{i18n 'Error'}}</div>
    {{/if}}
</li>
{{/each}}

Kind of a long topic, but I my opinion having standardized browser built-in templating would be very useful for a large number of sites and apps. Granted there are a lot of templating solutions developed both for regular JS and Web Components already in existence.

Does anyone know whatever happened with the Apple proposal or if there is anything planned related to templating?

Also if you have yourself come up with good templating solutions for complex apps or pages I would be very interested in hearing about it. For the moment when I develop with Web Components I'll be using JavaScript Template Literals, however it has too many limitations I feel for some apps.

bennypowers commented 3 years ago

See https://github.com/WICG/webcomponents/blob/gh-pages/proposals/DOM-Parts.md, wherein the previous proposal is split into two parts: DOM updates (addressed) and templating (explicitly bunted to userland)

ConradSollitt commented 3 years ago

Thanks for the info @bennypowers I didn't see the latest update.

I think I'll stick to the userland templating then. The DOM Parts proposal seems too simple to be used without adding a lot of extra JavaScript to an app. An example I can across when updating my personal site is I needed the tabindex on an image gallery to start at the correct number, but I want to make the update from JavaScript. Using userland templating (in my case JavaScript Template Literals) I am able to specify ${index+7} quickly from the HTML and change if needed based on other updates to the page. Perhaps, it will the DOM Parts propsoal will be useful but I guess it's just wait and see for now.

<template id="photo">
    <li class="${type}">
        <image-gallery image="img/photos/large/${image}" tabindex="${index+7}">
            <img src="img/photos/thumbnails/${image}" alt="${title}">
        </image-gallery>
        <p>${title}</p>
    </li>
</template>
ConradSollitt commented 3 years ago

I'll close this out since there are no plans to add a native templating language. No need for others to spend time on this issue in that case.