WebReflection / hyperHTML

A Fast & Light Virtual DOM Alternative
ISC License
3.07k stars 112 forks source link

Noob treeview example #42

Closed BentleyDavis closed 7 years ago

BentleyDavis commented 7 years ago

I'm learning HyperHTML and the new javascript it is using so I'm sure I'm missing some core concepts. This might also be a good example to have on a recursive template. I have a tree list appearing based on the data but I am having trouble making it interactive. I probably should use hyperHTML.wire instead of return in the renderNode function but I might also have it completely architected wrong. Any assistance would be appreciated. Here is a plnkr of what I have so far: https://plnkr.co/edit/RaPWEBwvdlLS0PVBmSiV?p=preview Here is a fork of your code: https://github.com/BentleyDavis/hyperHTML/blob/master/test/tree.html

WebReflection commented 7 years ago

This is how you should do (or one way of doing it) https://plnkr.co/edit/jwkp2erV7Chl5DXIq1Vv?p=preview

A literal template is exactly just a literal template, meaning if you don't use a tag, which is a function that will be implicitly invoked while the literal template is "resolved", nothing can possibly happen.

Or better, if you write the following:

function whatever() {
  return `this is a template`;
}

You'll simply return a template string as such, no matter how many interpolations you do there. In order to have a smarter mechanism, like hyperHTML, you need to tag the template.

// a tag example
function tag(statics, ...interpolations) {
  return statics.join('');
}

// returning such tag
function whatever() {
  return tag`this is a template`;
}

Accordingly, if you don't use a wire nothing will ever parse that children array or that onkeypress events.

A wire is a utility to create one or more elements in hyperHTML, it's like a document fragment, but it can be reused. A wire can, and when possible should, be related to a unique object/entity so that it can be reused instead of being parsed each time.

Using dictionaries in your case is a good example of what could be wired and it works indeed.

Last, but not least, to see something you need a node, like it is for a document fragment, and that bit was OK already, you rendered into a node through hyperHTML.

One extra thing though: you are not understanding the most basic rule of hyperHTML: if there are spaces around, that's textContent.

Accordingly, you cannot write the following expecitng the <ul> to be populated with wires

function renderNode(node) {
    return hyperHTML.wire(node)`
      <li>
      <input onkeypress="${events.onkeypress}">
      ${node.description}
      <ul>
        ${node.children.map((nodeId, i) => renderNode(dict[nodeId]))}
      </ul>
     </li>`;
}

You need to avoid spaces when you want HTML as string, nodes, or list of strings and list of nodes, including wires.

function renderNode(node) {
    return hyperHTML.wire(node)`
      <li>
      <input onkeypress="${events.onkeypress}">
      ${node.description}
      <ul>${
        node.children.map((nodeId, i) => renderNode(dict[nodeId]))
      }</ul>
     </li>`;
}

More than writing this as first point in every single documentation page I don't honestly know how to improve because it looks like everyone don't understand such simple rule so, hints welcome.

Hope I've clarified a bit. If not, ask me more, thanks.

BentleyDavis commented 7 years ago

Thanks so much. That is very helpful.

To dig a little deeper into the spaces issue: Why does the ${node.description} work in the last example?

BentleyDavis commented 7 years ago

One last item. How do I pass the object into the onKeyPress function like the current node?

WebReflection commented 7 years ago

as soon as there is something around, that is a textContent

function greetings(render, name) {
  render`<h1>Hello ${name}!</h1>`;
}

greetings(
  hyperHTML.bind(document.body),
  'BentleyDavis'
);

That is another node which content will be BentleyDavis, not a single innerHTML call will be used.

It is indeed easy to have text inside a content, but there's no way to differentiate with fragments, which requires zero surrounding chars.

Example:

function greetings(render, name) {
  render`<h1>${
  stringOrNodeOrWireOrArray
  }</h1>`;
}

function greetings(render, name) {
  render`<h1>
  ${onlyTextContentNotEvenHTML}
  </h1>`;
}
WebReflection commented 7 years ago

How do I pass the object into the onKeyPress function like the current node.

like you would do with any other architecture. Either attach it to the object or pass it as an argument?

This has nothing to do with hyperHTML which is simply based on functions calls, right? That's just JavaScript 😉

WebReflection commented 7 years ago

Also: Getting Started and Deep Dive will surely answer everything else.

The concept is sol simple, it's just new and unusual at first glance.

BentleyDavis commented 7 years ago

It's definitely just javascript. Just not an area I have worked with a lot. No need for reply, but, I'm trying to pass the info object as an argument with <input onkeypress="${events.onkeypress(info)}"> but it fires immediately. It looks like I have to do something like <input onkeypress="${events.onkeypress.bind(info)}">

WebReflection commented 7 years ago

Yes, that's the way I'd go <input onkeypress="${events.onkeypress.bind(info)}">

at the end of the day that callback will receive the event object with a .target that points to the input so using the this context to reach other info is just OK.

Another way that won't require the creation of N bound functions is to pass that nodeId value instead of the object directly so you can store it and retrieve it any time you want

`<input data-id="${nodeId}" onkeypress="${events.onkeypress}">`