Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jcormont/async-html-template

Simple async/await-able HTML template engine
https://github.com/jcormont/async-html-template

async asynchronous await express expressjs html renderer view-engine

Last synced: 13 days ago
JSON representation

Simple async/await-able HTML template engine

Awesome Lists containing this project

README

        

# Async HTML Template Engine

A simple asynchronous (using async/await) HTML template engine, for use with Express or as a standalone HTML generator.

> **Note:** this module is ONLY for use with NodeJS 8+ (for native async/await), and does NOT work in the browser.

## Usage (Express)

Register the view engine as follows:

```javascript
app.engine("html", require("async-html-template").AsyncHtmlViewEngine);
app.set("view engine", "html");
```

(or, in TypeScript) --

```typescript
import { AsyncHtmlViewEngine } from "async-html-template";
app.engine("html", AsyncHtmlViewEngine);
app.set("view engine", "html");
```

Then, use Express views to render content:

```javascript
app.get("/", (req, res) => {
res.render("index", { title: "Hello, world!" });
})
```

## Standalone Usage

You can also use the template engine without Express.

For simple one-off transforms, use the exported `renderAsync` and `renderFileAsync` functions:

* `renderAsync(templateText, context)` returns a Promise for rendered HTML based on given template string and context object.
* `renderFileAsync(fileName, context)` returns a Promise for rendered HTML based on the template in the given file and with the given context object.

The exported `Template` class can be used directly as well. It encapsulates an asynchronously compiled function that efficiently generates HTML output for a given context object.

```javascript
// render a template from a file:
let fileTemplate = Template.fromFile("./index.html");
let fileResult = await fileTemplate.renderAsync({ title: "Hello, world!" });

// render a template from a string:
let str = `

{{ title }}

`;
let strTemplate = new Template(str);
let strResult = await strTemplate.renderAsync({ title: "Hello, world!" });
```

## Optimization

Templates that are generated by `renderFileAsync` and `Template.fromFile` (but NOT the partials included by any of these templates) are automatically _cached_, so that files are generally only read and compiled only once.

The resulting HTML is never cached, even for the same context object.

By default, HTML output is minified and comments are removed (using the NPM package `html-minifier`). You can supply minification options to the `renderAsync` method of the `Template` class, _or_ use the exported `defaultHtmlMinifierOptions` object which contains default options.

## Templates

A template is basically just (partial) HTML, with added code that runs while rendering the template.

### Expression Tags

Tags that look like `{{ ... }}` contain JavaScript expressions that are evaluated while rendering. For each tag, the result is HTML-encoded and inserted in place of the tag.

As part of the expression, you can use properties from the context object (passed in through Express `.render`, or any of the `Template` rendering methods), as if they are variables. Alternatively you can use the `context` object explicitly, e.g. if a property is masked by a local variable or if its name is a reserved word.

```html

Hello, {{ firstName }}


Your case number is {{ context.case.getNumber() }}


```

Use the `` tag described below if you do not want the result of an expression to be HTML-encoded.

### Template Script Code

Any script code inside of a script tags that contains the `in-template` attribute is not copied to the output, but is run while rendering instead.

You can use properties from the context object in template script code as well.

```html

let result = [];
for (let i = 0; i < firstName.length; i++) {
result.push(firstName.charCodeAt(i));
}

Your name in Unicode is {{ JSON.stringify(result) }}


```

### Loops

Wrap a part of your template in a `` ... `` tag to add a for-loop around the wrapped area of your template, with exactly the same syntax as in JavaScript.

```html



{{ i }}



{{ property }}: {{ person.attributes[property] }}



var fib = [1, 1, 2, 3, 5, 8, 13, 21];


{{ n }}



{{ n }} ({{ index }})


```

For a while-loop, you can use the `` tag.

```html


Fib in reverse is
{{ fib.pop() }}


```

### Conditionals

Wrap a part of your template in a `` ... `` tag to check if a condition is true _while rendering_, and skip rendering the wrapped area if not.

```html



Children



Name: {{ child.name }}





```

Optionally, you can leave out the braces from the value of the `if` attribute (i.e. `if="expression"`) for exactly the same result.

You can also combine conditionals with loops in the same `` tag.

```html



Child: {{ child.name }}




```

### Literal HTML Output

For an expression that returns HTML which should be appended literally (instead of being HTML-encoded), you can use the `` tag.

This tag does _not_ wrap the output in a surrounding tag.

```html

let myHtml = "<b>Hello</b>";

This is HTML-encoded: {{ myHtml }}


This is not:


```

### Reusable Templates (Mixin Functions)

Using the `` tag, you can define a named template (sometimes called a 'mixin' function) that can be used multiple times throughout the rest of your template. This does in fact define an (asynchronous) function within the compiled template, which can be passed around and called using its identifier.

To make it easier to call functions defined in this way, you can use the `` tag. If you include any content within this tag, the resulting HTML will be passed to the defined function in a `content` property (also available as `context.content`).

```html



await new Promise(r => setTimeout(r, 1000));

One second later.

```

Alternatively, you can pass in a different context object, using the `context="..."` attribute. The result of the expression is used as the function's context.

```html
Hello, {{ name }}!

```

### Partial Includes

You can include (partial) HTML templates from other files using the `` tag. Any given file name is considered to be relative to the current file -- so this only works if the template is loaded from a file in the first place (i.e. not using the `renderAsync(templateText, context)` function, for example).

This tag can also be combined with conditionals or loops, which causes the partial template to be evaluated repeatedly and/or conditionally.

If you need the partial template to be rendered with a different context object, you can provide the `context="..."` attribute. The result of the expression is used as the partial template's context, which can be different for each iteration of a combined loop (if any).

If you do not specify a context expression, a shallow clone of the current context object will be used as the context for the included partial, along with wrapped HTML content (if any) in the `content` property.

```html

```

Note that any _variables_ set in the current template scope (i.e. using `let` or `var`) are not available to the partial template. You **can** set properties of the `context` object, which will also be available to partial templates with the same context:

```html


// add current child to context object
context.child = child;

```

Just like with template functions defined using ``, you can pass contained content along to the partial template. The content will end up in the `content` property of the partial's context. For this reason, the `partial` attribute is also aliased as `wrap`, since it can be used to wrap sections into an outer 'template'.

Any functions defined in your containing code up to the point of the `` tag will also be passed to the partial template (unless you explicity specify another context object). These functions can be used by the partial template using the `` tag.

```html

console.log("foo")

Content


Lorem ipsum.



```

## Asynchronous Code

Because of the asynchronous nature of this template engine, you can actually include `await` expressions in your template code. This is really helpful if your (view) model has `async` features, which can be consumed directly by the template, instead of having to `await` any data that is used inside of your template.

Given a model that looks like this:

```typescript
export class WaitForIt {
static async getSomeDataAsync() {
// do something useful here, like loading data from a server...
await new Promise(res => setTimeout(res, 1000));
return { foo: ["Bar", "Baz", "Quux"] };
}
}
```

You can use `await` expressions for this model's results directly in a template:

```html




let data = await WaitForIt.getSomeDataAsync();


{{ name }}



{{ name }}


```