JavaScript: Template arrays experiment
Tags: showdev, javascript, webdev, experiment
Published: 2020-03-14 17:27:06


## The idea:
Use JavaScript **arrays** instead of **template strings** to render some HTML.
I created a tiny library called [template arrays](https://github.com/slavcoder/template-arrays) to show this experimental concept.

## Example
Lets consider simple **article** template.
We need to show the following data:
- article title
- author name
- article content
- views (if more than 100, we want to add class **-popular** to article)

The expected result:

```html
<article class="article -popular">
    <header class="article_header">
        <h2 class="article_heading">Lorem ipsum</h2>
        <p class="article_author">John Doe</p>
        <p class="article_views">views: 102</p>
    </header>
    <div class="article_content">
        Lorem ipsum dolor sit, amet consectetur adipisicing elit. 
        Minima deserunt officia laboriosam eligendi id assumenda 
        veritatis ad dolore voluptatibus quas?
    </div>
</article>
```

## Requirements:
- easy way to change attributes
- easy way to add variables (text or other elements inside template)

## Implementation

This could be our test article data:
```js
const data = {
    title: 'Example Title',
    author: 'slavcoder',
    views: 125,
    content: 'lorem ipsum dolorem lorum'
}
```

## Comparison
Implementation of simple **article** could look like this:

### Using template string

```js
function getArticle(data) {
    const popular = data.views > 100 ? '-popular' : ''

    return `
        <article class="article ${popular}">
            <header class="article_header">
                <h2 class="article_heading">${data.title}</h2>
                <p class="article_author">${data.author}</p>
                <p class="article_views">views: ${data.views}</p>
            </header>
            <div class="article_content">
                ${data.content}
            </div>
        </article>
    `
}

document.body.innerHTML += getArticle(data) 

```

### Using template arrays


```js
import render from 'template-arrays'

const article = [
    ['article', {attr:'article', class:'article'}],
        ['header', {class:'article_header'}],
            ['h2', {class:'article_heading'}],{var:'title'},['/h2'],
            ['p', {class:'article_author'}],{var:'author'},['/p'],
            ['p', {class:'article_views'}],'views: ',{var:'views'},['/p'],
        ['/header'],
        ['div', {class:'article_content'}],
            {var:'content'},
        ['/div'],
    ['/article']
]

document.body.innerHTML += render(article, {
    attr: {
        article: {
            class: data.views > 100 ? '-popular' : ''
        }
    },
    var: {
        title: data.title,
        author: data.author,
        views: data.views,
        content: data.content
    }
}) 

```
## Template arrays - how it works

- Add attributes in object, just after tag name: <code>['p', {class:'paragraph', id:'test1'}]</code>
- To define variables, set literal with **var** property: <code>{var:'name'}</code>
- If you want to edit some attributes later, set **attr** property: <code>['p', {attr:'name'}]</code> (just like other attributes above)

The render function takes two arguments: array with template and object with two following properties: 
- **attr** (used to set or update attributes)
- **var** (used to add some text or other template)

```js
const example = [
    ['div', {attr:'container', id:'container', class:'className1'}],
        {var:'insideElement'},
        {var:'insideText'},
        'just text',
    ['/div']
]

const exampleHTML = render(example, {
    attr: {
        container: {
            'data-info': 'data attribute example',
            id: 'new-id',
            class: 'className2 className3',
            style: `
                color: red;
                display: block;
                width: 250px;
            `
        },
        spanInside: {
            id: 'inside-span-id',
            class: 'inside-span-class',
            style: 'color: blue;'
        }
    },
    var: {
        insideElement: [
            ['span', {attr:'spanInside'}],
                'element text ',
            ['/span']
        ],
        insideText: 'simple text '
    }
})

document.body.innerHTML += exampleHTML
```

What if you won't define **attr** or **var** in render function but used in template? It will be simply ommited, no worries.

## Advantages

Even though it may look more complicated than template strings, I think it gives more clarity and flexibility.

- you can split templates to parts, just like below:


```js
const header = [
    ['header', {class:'article_header'}],
        ['h2', {class:'article_heading'}],{var:'title'},['/h2'],
        ['p', {class:'article_author'}],{var:'author'},['/p'],
        ['p', {class:'article_views'}],'views: ',{var:'views'},['/p'],
    ['/header']
]

const article = [
    ['article', {attr:'article', class:'article'}],
        ... header,
        ['div', {class:'article_content'}],
            {var:'content'},
        ['/div'],
    ['/article']
]
```
- you can save template arrays in .json files and still use **attr** and **var**
- you can easily change attributes in render time
- it is also pretty fast (**check out my test** on this [example](https://slavcoder.github.io/template-arrays/) - render up to 10 000 elements)

## Links
If you found it interesting, check out my repository and live example
- [template arrays repository](https://github.com/slavcoder/template-arrays)
- [live example](https://slavcoder.github.io/template-arrays/)
- [live example code](https://github.com/slavcoder/template-arrays/tree/master/example/)

Thanks for reading about my experiment :)
Have a nice day!
Sławomir.