DEV Community

Cover image for Easy apps with hyperHTML — 4, wire types and intents
Ivan
Ivan

Posted on • Updated on

Easy apps with hyperHTML — 4, wire types and intents

Version en español

Part 4 written by

  1. Introduction, wire/bind
  2. Events and components
  3. Moar about components and simple state management
  4. Wire types and custom definitions (intents)
  5. Custom elements with hyper
  6. Customizing my custom elements
  7. Testing!
  8. Async loading, placeholder and a Typeahead with hyper
  9. Handling routes
  10. 3rd party libraries

In previous parts of this tutorial, we’ve seen how to use components with hyperHTML. We built a simple table that could sort, but now it’s time to look at some additional features hyperHTML brings us. The feature we’ll look at next is the ability to customize wire(). With a custom wire() function, we’ll be able to transform our templates, or load parts of them asynchronously. Before we dive into this, let’s explore the standard wire() function a little more.


Built-in Types

HyperHTML wire() comes with two built-in types. Its default is html, but hyperHTML also supports svg. As we’ve seen, using the html type requires no additional work on our part. However, if we want to wire an svg node, we need to explicitly say so:

hyperHTML.wire(data, 'svg')`<rect width=${data.width} height=${data.height} />`
Enter fullscreen mode Exit fullscreen mode

The second parameter passed to wire is the type. Let’s see the example running:

Recall all the way back to part 1. In our clock example, we used wire() two ways. First, we used it without a reference:

hyperHTML.wire()`…`
Enter fullscreen mode Exit fullscreen mode

But then we also used it by passing an object to the function:

hyperHTML.wire(user)`…`
Enter fullscreen mode Exit fullscreen mode

In that first example we mentioned that the object passed to wire() will not be updated each time it renders. This is because by passing it to wire() we are creating a a relationship between wire and the contents of the object. This relationship does more than just speed up renders, we can also re-use this object in different places, with different templates. We might then give this object an id for each place we want to use it:

hyperHTML.wire(user, ':address')`…`
hyperHTML.wire(user, ':profile')`…`
hyperHTML.wire(user, ':login')`…`
Enter fullscreen mode Exit fullscreen mode

We can also specify a type alongside the id:

hyperHTML.wire(user, 'svg:avatar')
Enter fullscreen mode Exit fullscreen mode

Content Values

We also talked a bit about the different content values in part 1. Let’s dive deeper into them now.

Think about template literals. Inside a template literal we use ${…} to evaluate expressions. Whatever expression you pass into the ${…} will be added to your template depending on it’s evaluated type. For example, hyperHTML is injection-safe by default since passing strings will be injected to the template as textContent.

<p>Hola ${'<script src="http://badguy.com"></script>'}</p> // whew, safe!
Enter fullscreen mode Exit fullscreen mode

But you can also force it to be text. In order to do that, you have to pass an object to hyperHTML:

<p>Hola ${{text: 'Mundo'}}</p>
Enter fullscreen mode Exit fullscreen mode

And yes, if you pass a node it will be appended:

<p>Hola ${node}</p>
Enter fullscreen mode Exit fullscreen mode

or you can force it by passing an object, like above:

<p>Hola ${{html: '<strong>Mundo</strong>'}}</p>
Enter fullscreen mode Exit fullscreen mode

You can even pass a promise! If you do pass a promise, then whenever the promise is resolved it will be resolved to any of the understood types. There is also a type, any. Any usually takes a promise, but can take any other type as well, and hyperHTML will try to match it.

See them in action:

But wait, that’s not all! You can also pass arrays! The only thing to keep in mind is the items in the array must be of the same type: strings or numbers or nodes, or even a list of promises. You’ll get unexpected results if your array is populated with different types.

We typically use promises when we don’t have data now, but are promised to have in the future. HyperHTML provides a placeholder that displays while the data is loading. Let’s see an example.

Attributes

Element attributes can be regular attributes, booleans, or events. If you’re coming to hyperHTML from another framework, you might be expecting to use partial attributes, but you don’t actually need them. There are two more attribute types we need to talk about — style and data. These attributes will help us easily build interfaces.

The style attribute can take a string like any other attribute, or you can also pass an object:

wire(ref)`<p style=${{fontSize: 32}}>${'BIG CONTENT'}</p>`;
Enter fullscreen mode Exit fullscreen mode

The data attribute lets you pass raw JavaScript data to an element:

wire(ref)`<p data=${user} />`;
Enter fullscreen mode Exit fullscreen mode

Let’s see them in action


Custom types

We can tap into that object functionality and create our custom render parts. And depending on the name, it will be interpreted as an attribute (if it has ‘-’), or as a new intent.

Custom intent

To define a new type, we will use hyperHTML.define() . Convenient, right? Define() takes two parameters, a string name and a callback.

define(intentName, callback)
Enter fullscreen mode Exit fullscreen mode

The callback receives any interpolated data as parameters, and returns data that we’ll use in the display. The callback can return any of the known data types.

For example, let’s define an intent for dates. Whenever we receive a date, we will return a nicely formatted date. Let’s also use an asynchronous intent:

HyperHTML will first try to match on any known intents like html, text or any. If it cannot find a match, it will try with the ones it has in its registry, that is, the ones defined with hyperHTML.define(). If hyper finds it there, it will use your custom intent.

Custom attributes

As mentioned before, if you define the intent with a ‘-’ in the name, it will be treated as an attribute. This means we can add any custom attribute to any element.

Whoah. Seriously?

Yep, you bet.

In our next example, the callback will receive the node element and whatever value passed to the attribute. If you return something, it will be used as the value for the attribute. Be sure to note, that to make the custom attribute work you must use ${…} for the attribute value.


With what we now know, let’s update our table from part 3.

We’ll add an svg icon, so we know what column is currently sorted and if it’s ascending or descending. Let’s also update our data array with more accurate user information, as well as a date so we can put our date intent to good use.

We are using the Octicons icon pack. Now for the rest of the code. Be especially sure to take a look at the Header and the Table files.


Sweet! Now you know how powerful hyperHTML can be. This is pretty much everything there is to learn about the library itself, but we are going to tap into some other utilities provided by Andrea Giammarchi to add more tools to our belt. With all these tools combined, we’ll be able to create awesome and high-performing web applications.

Top comments (0)