hyperHTML wire vs string

925 views Asked by At

I am starting to use hyperHTML have a question

starting with

const data = [1,2,3]

Using wire

hyperHTML.bind(document.getElementById('root'))`
  <h1>Hello, world!</h1>
  ${ data.map( num => hyperHTML.wire()`<li>${num}</li>`  ) }
`;

Using string

hyperHTML.bind(document.getElementById('root'))`
  <h1>Hello, world!</h1>
  ${ data.map( num => `<li>${num}</li>`) }
`;

Why is wire better?

When wire has no "id" (obj,string) it will return an element without references

Here is the documentation but they dont say why one should use wire over string.

Thanks for any help


Edit:

Just thinking.. would define be better to use? Something like:

hyperHTML.define(numberListItem, num => `<li>${num}</li>`)

hyperHTML.bind(document.getElementById('root'))`
  <h1>Hello, world!</h1>
  ${ data.map( num => ${{numberListItem: num}}) }
`;

But now you would fill up the name space of every small element :(

1

There are 1 answers

1
Andrea Giammarchi On BEST ANSWER

The main problem in your question is the example itself: a list of primitives, in this case numbers, used as generic element content.

This is not a so common real-world use case, where numbers comes from data and data is weakly referenciable.

However, if it's exactly a list of few numbers that you want inject as LI elements, hyperHTML lets you do that, and if that's all you need, just go for it.

Now let me try to explain your question:

Why is wire better?

hyperHTML gives you a way to define various kind of content, including:

  1. text, which is automatically sanitized for you
  2. attributes, including events
  3. partial portions of the element that could be resolved lazily

You might want/need to use hyperHTML features to create elements as one-off operation.

A form, an input, a button, an image, if you want to quickly create a DOM element, you can do that via hyperHTML because it doesn't lock you in, it's rather an opt-in abstraction/library.

That is why you could relate a specific data object to a wire, but you could also simply use wires without references: it's OK for quick prototyping or small amount of changes.

Now let me show you few examples from that (reduced) list of features.

Point 1: text

If instead of plain numbers you have a list of book titles, what will your unwired code produce?

const data = [
  'hyperHTML is the best',
  '<HTML> 5 Best Practices'
];
// without wire
hyperHTML.bind(document.body)`
  <h1>List of books:</h1>
  <ul>
  ${data.map( text => `<li>${text}</li>` )}
  </ul>`;

The answer is that the second book will cause layout issues due <HTML> tag, while using wire() the title will be shown as expected.

Automatically sanitized layouts are then a benefit of a one-off wire.

Point 2: attributes

Consider this case:

wire()`<input value=${any} disabled=${!editable} onchange=${react}>`

That is nothing you can create as string for these reasons:

  1. the value might contain undesired chars, so that the output might fail
  2. the disabled attribute will be there and no matter what you pass in, the input will be disabled
  3. the onchange event won't ever be set as expected

Accordingly, wire()... is a more convenient way to create an element.

Point 3: lazy content

const data = [
  'hyperHTML is the best',
  '<HTML> 5 Best Practices'
];
// without wire
hyperHTML.bind(document.body)`
  <h1>List of books:</h1>
  <ul>
  ${data.map(title => `<li>
    ${title}
    ${fetch(`/books-info.php?title=${encodeURIComponent(title)}`)
      .then(b => b.json())
      .then(info => info.stars)
      .catch(err => 'not available')}
  </li>`)}
  </ul>`;

Above example will fetch stars/rates from an end point and show them in place once resolved.

The layout will be filled up as rates get resolved.

About your Edit

The define method makes sense only if you represent a way to resolve the value.

Using an Array as define key is really not the best way to go.

If you need to update the same data over and over, you can use that data as a reference and pass keys as IDs.

Keys/IDs could be the index of the data, an info however unrelated with the current list item, or something more unique, like the book title or, more generically, the data primitive content, assuming it's unique.

Your initial code would then look like the following:

hyperHTML.bind(document.getElementById('root'))`
  <h1>Hello, world!</h1>
  ${ data.map(
    num => hyperHTML.wire(data, ':' + num)`<li>${num}</li>`
  ) }
`;

The wire() signature is indeed to accept a reference object you'd like to relate to some layout, and also a layout type and/or its id.

These are all valid calls:

wire()
wire(info)              // info => (html default) node
wire(info, 'html')      // info => html node
wire(info, 'svg')       // info => svg node
wire(info, ':uid')      // info => (html default) node mapped as uid
wire(info, 'html:uid')  // info => html node mapped as uid
wire(info, 'svg:uid')   // info => svg node mapped as uid

With these primitives you can relate any list of information to a specific node. It's the keyed concept from React or Vue on steroids.

Map your own wire

If none of the above mechanism satisfies your requirements, you can always create your own wires and use them as you like.

const data = [1,2,3];
const wires = data.reduce(
  (w, num) => {
    w[num] = wire()`<li>${num}</li>`;
    return w;
  },
  {}
);
hyperHTML.bind(document.getElementById('root'))`
  <h1>Hello, world!</h1>
  ${ data.map( num => wires[num]) }
`;

In above eaxample you could even data.sort() and still obtain the right LI for the right number.

Real-world use cases

I hope you agree the most common situation is that the source of information, your data array, is quite often, if not always, an array of objects.

const data = [
  {title: 'a book', author: 'an author'},
  {title: 'another book', author: 'another author'}
];

In this case you just wire the book info, and let everything else work as expected without trashing nodes or injecting LIs on the wild.

const {bind, wire} = hyperHTML;
bind(document.body)`
  <h1>List of books:</h1>
  <ul>
  ${data.map(book => wire(book)`
    <li>
      <strong>${book.title}</strong>
      by ${book.author}
    </li>
  `)}
  </ul>`;

I hope it's now clear why anyone, and any library or third parts project, could benefit from hyperHTML wires, regardless if the rendering bit is an hyperHTML bound node.