2.4.4 • Published 2 years ago

templize v2.4.4

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

templize

HTML reactive template parts for any DOM elements with expressions and directives.

Based on Template Instantiation and DOM-parts specs.

Features

Extends template parts with the following:

  • Works with any elements, not just <template>;
  • Supports reactive fields;
  • Provides expression processor;
  • Enables loops, conditions;
  • Exposes directives API;
  • Plugs as vanilla ESM, doesn't require tooling.

Usage

It can be used as module via npm i templize or in HTML directly:

<script type="importmap">{ "imports": { "templize": "path/to/templize.js" }}</script>

<div id="foo" class="foo {{y}}">{{x}} world</div>

<script type="module">
  import templize from 'templize'

  templize(document.getElementById('foo'), { x: 'Hello', y: 'bar'})
  // <div id="foo" class="foo bar">Hello world</div>
</script>

API

const [params, update] = templize(element, init?);

params is proxy reflecting template fields values. Changing any of its props updates / rerenders fields. update can be used for bulk-updating multiple props. init is the initial state to render the template. It can include reactive values, see reactivity.

Reactivity

Template fields support the following async/reactive values:

  • Promise/Thenable
  • AsyncIterable
  • Observable/Subject

Update happens when any param changes:

<div id="done">{{ loading ? '...' : result }}</div>

<script type="module">
  import templize from 'templize'
  import { signal } from '@preact/signals'

  const loading = signal(false), result.value = signal(false)
  templize(document.querySelector('#done'), { loading, result })
  
  setTimeout(() => (loading.value = true, result.value = 'done'), 1000)

  // <div id="done">...</div>
  // ... 1s after
  // <div id="done">done</div>
</script>

This way, for example, @preact/signals or rxjs can be streamed directly to element attribute or content.

Note: observers don't require disposal, since they're connected in weak fashion. Once element is disposed, observables are disconnected.

Expressions

Templize enables expressions via default expression processor:

<header id="title">
  <h1>{{ user.name }}</h1>
  Email: <a href="mailto:{{ user.email }}" onclick="{{ e => { event.preventDefault(); await sendEmail(user.email); } }}">{{ user.email }}</a>
</header>

<script>
  import templize from 'templize';

  templize(
    document.querySelector('#title'),
    { user: { name: 'Hare Krishna', email: 'krishn@hari.om' }}
  )
</script>

It supports the following field expressions with common syntax:

PartExpression
Value{{ foo }}
Property{{ foo.bar?.baz }}, {{ foo[bar] }}
Call{{ foo.bar(baz, qux) }}
Boolean{{ !foo && bar \|\| baz }}
Ternary{{ foo ? bar : baz }}
Primitives{{ "foo" }}, {{ true }}, {{ 0.1 }}
Comparison{{ foo == 1 }}, {{ bar >= 2 }}
Math{{ a * 2 + b / 3 }}
Pipe{{ bar \| foo }}{{ foo(bar) }}
<!-- Functions{{ e => foo() }}, {{ async e => { await foo(); bar(); } }} -->
<!-- Loop{{ item, idx in list }}params.dUsed for :for directive only -->
<!-- Spread{{ ...foo }}params.fooUsed to pass multiple attributes or nodes -->

Attributes

Processor makes assumptions regarding how attribute parts set values.

  • hidden="{{ boolean }}" boolean values set or remove attribute.
  • onClick="{{ function }}" assigns onclick handler function (no need to call it, unlike in html).
  • class="{{ classes }}" can take either an array or a string.
  • style="{{ styles }}" can take either an object or a string.

Other attribute values cast to strings.

Directives

Templize recognizes shortcut directives via :<attr> (similar to vue).

Loops

Iterating over set of items can be done with each directive:

<ul>
  <li :each="{{ item, index in items }}" id="item-{{item.id}}" data-value="{{item.value}}">{{item.label}}</li>
</ul>

Conditions

To optionally display an element, there are if, else-if, else directives.

<span :if="{{ status == 0 }}">Inactive</span>
<span :else-if="{{ status == 1 }}">Active</span>
<span :else>Finished</span>

Note: text conditions can be organized via ternary operator:

<span>Status: {{ status === 0 ? 'Active' : 'Inactive' }}</span>

Adding directives

To register a directive, directive(name, onCreate) function can be used:

import templize, { directive } from 'templize'

directive('inline', (instance, innerTplPart, state) =>
  innerTplPart.replaceWith(innerTplPart.template.createInstance(state))
)

Interop

Templize supports any standard template parts processor:

const params = templize(element, initState, {
  createCallback(element, parts, state) {
    // ... init parts / parse expressions
  },
  processCallback(element, parts, state) {
    // ... update parts / evaluate expressions
  }
})

Any external processor can be used with templize, eg. @github/template-parts:

import templize from 'templize'
import { propertyIdentityOrBooleanAttribute } from '@github/template-parts'

const params = templize(
  document.getElementById('foo'),
  { x: 'Hello', hidden: false },
  propertyIdentityOrBooleanAttribute
)
params.hidden = true

Templize expression processor can also be used with other template instancing libraries as:

import { TemplateInstance } from '@github/template-parts'
import { processor } from 'templize'

const instance = new TemplateInstance(document.querySelector('my-template'), {}, processor)

Or it can be used with proposal polyfill:

import 'templize-instantiation-polyfill'
import { processor } from 'templize'

document.defineTemplateType('my-template-type', processor)

Dependencies

Buddies

  • spect − selector observer, perfect match for organizing flexible native DOM templates.
  • value-ref − reactive value container with reactivity, useful for state management.
  • subscribable-things − reactive wrappers for various APIs.

Neighbors

  • stampino − small HTML template system based on lit-html.
2.4.1

2 years ago

2.4.3

2 years ago

2.4.2

2 years ago

2.4.4

2 years ago

2.4.0

2 years ago

2.3.0

2 years ago

2.2.0

2 years ago

2.1.2

2 years ago

2.1.1

2 years ago

2.1.0

2 years ago

2.0.0

2 years ago

1.5.0

2 years ago

1.4.3

2 years ago

1.4.2

2 years ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.1

2 years ago

1.2.0

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.0

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago