Skip to content

Latest commit

 

History

History
249 lines (179 loc) · 5.43 KB

README.md

File metadata and controls

249 lines (179 loc) · 5.43 KB


A prototyping library for JSON-driven web pages.

A quick example

Let's say you keep a list of things you like in a file called favorites.json. It looks like this.

[
  "Raindrops on roses",
  "Whiskers on kittens",
  "Bright copper kettles",
  "Warm woolen mittens",
  "Brown paper packages tied up with strings"
]

And, you want to display this list on a website. HTML has a <template> tag, so let's use that.

<h1>My favorite things</h1>
<ul>
<template data-src="/favorites.json" embed>
  <li slot></li>
</template>
</ul>
<script type="module" src="https://unpkg.com/fill-me-in"></script>

The data-src attribute specifies the data source for this template. The embed attribute tells the library to render the template in place.

So, the HTML above produces

My favorite things

  • Raindrops on roses
  • Whiskers on kittens
  • Bright copper kettles
  • Warm woolen mittens
  • Brown paper packages tied up with strings

Ready to try it out for yourself? Have a look at the demos in the next section.

Demos

Tests

The Render API

The class Render is a builder API for customizing the render operation.

For example, this expression,

render("#album-template")
  .filter(album => album.rating >= 4.5)
  .into("#content");

When executed (via into), does the following:

  • Finds the DOM element by the ID album-template
  • Fetches JSON from the URL specified in its data-src attribute
  • Removes albums that have a rating lower than 4.5
  • Renders the remaining albums with the #album-template and inserts it into #content

render(template: string | HTMLTemplateElement): Render<any>

Initialize the Render API with a selector string or HTMLTemplateElement.

.map<U>(f: T => U): Render<U>

Map over content, transforming it with f.

.mapList<U, V>(this: Render<U[]>, f: (u: U) => V): Render<V[]>

Map over a list, transforming each item into something else.

.reduce<U, V>(this: Render<U[]>, f: (accumulator: V, next: U) => V, initial: V): Render<V[]>

Fold over the content to transform it into something else.

.filter<U>(this: Render<U[]>, predicate: (value: U) => boolean): Render<U[]>

Remove content, keeping only that which matches the predicate.

.withValue<T>(value: T): Render<T>

Specify values statically, instead of from data-src.

.asFragment(): Promise<DocumentFragment>

Runs the render process with the customizations.

.into(target: string | HTMLElement): Promise<DocumentFragment>

Runs asFragment and inserts the document fragment into the target, replacing its contents.

.cache(): Promise<Render<A>>

Runs the renderer, and creates a save point for its state.

Common scenarios

Some common scenarios follow.

Lists

A list of albums

{
  albums: [
    { name: "Dr. Feelgood", year: 1989, artist: "Mötley Crüe" },
    { name: "Appetite For Destruction", year: 1987, artist: "Guns N' Roses" }
  ]
}

applied to this template

<template>
  <table slot="albums">
    <tbody>
      <template>
        <tr>
          <td slot="name"></td>
          <td slot="year"></td>
          <td slot="artist"></td>
        </tr>
      </template>
    </tbody>
  </table>
</template>

produces

<table>
  <tbody>
    <tr>
      <td>Dr. Feelgood</td>
      <td>1989</td>
      <td>Mötley Crüe</td>
    </tr>
    <tr>
      <td>Appetite For Destruction</td>
      <td>1987</td>
      <td>Guns N' Roses</td>
    </tr>
  </tbody>
</table>

Empty lists

The attribute onempty is a Javascript callback that is run when the indexed list is empty (i.e. { albums: [] }).

<div class="or-empty" style="display: none">No albums found.</div>

<template>
  <table slot="albums" class="or-empty" onempty="$('.or-empty').toggle()">
    <tbody>
      <template>
        <tr>
          <td slot="name"></td>
          <td slot="year"></td>
          <td slot="artist"></td>
        </tr>
      </template>
    </tbody>
  </table>
</template>

becomes

<div class="or-empty">No albums found.</div>

<table class="or-empty" style="display: none">
  <tbody>
  </tbody>
</table>

Attribute slots

The indexed value sets the src attribute.

{
  pic: "https://example.com/example.jpg"
}

applied to

<img slot-src="pic">

produces

<img src="https://example.com/example.jpg">

Mods (short for modifiers)

HTML has a nested structure that maps to JSON, but sometimes we need more flexibility. For <img>, we want to set the value of the src attribute. For <a> we want to set the value of href and textContent. The render function knows how to do all of this because of mods. Mods describe how to transform a target element by a value.

The default mod sets the textContent of the target.

function(e) { e.target.textContent = e.value }

You can define your own custom mods. This is a nonsense modifier to set every target element to "hello", ignoring the passed in value.

function nonsense(e) { e.target.textContent = "hello" }

Pass it to render via withMods.

renderFragment.withMods(function(mods) { return [nonsense]; });