WebReflection / hyperHTML

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

Wire that starts with initial existing dom element and acts as an outer patch on subsequent renders #38

Closed matthewrobb closed 7 years ago

matthewrobb commented 7 years ago

Basically I have markup that exists in the dom and I want to use a template to patch it and it's attributes AND it's children. I feel like what I am looking for is some combination of wire and bind but I can't seem to hack my way to get it going, any suggestions?

joshgillies commented 7 years ago

Is this in the context of a SSR app or similar? In which case, what you're after might be something along the lines of:

<div id="app">
  <div>
    <button>Hello World!</div>
  </div>
</div>
const hyperHTML = require('hyperhtml')

const root = hyperHTML.bind(document.getElementById('app'))
const app = hyperHTML.wire()`
  <div>
    <button onclick="${(e) => console.log('clicked')}">Hello there!</div>
  </div>
`
setTimeout(() => root`${app}`, 2000)
WebReflection commented 7 years ago

beside @joshgillies hint produces Hello there! instead of Hello World!, I agree the way to go should be similar for your case @matthewrobb

matthewrobb commented 7 years ago

So I'd have to use a wrapper element to do what I want? I just want to be able to upgrade an existing node tree against a template. I'd have to pull it into a wrapper element and then render against the wrapper.

joshgillies commented 7 years ago

considering your request, is there no chance of having an existing container you can target? not even <body>?

If not, assuming the above static markup you could do something along the lines of:

const hyperHTML = require('hyperhtml')

const container = document.createElement('div')
container.appendChild(document.getElementById('app'))
const root = hyperHTML.bind(document.body.appendChild(container))

const app = hyperHTML.wire()`
  <div id="app" class="${'boom'}">
    <div class="${['static', 'dynamic'].join(' ')}">
      <button onclick="${(e) => console.log('clicked')}">Hello there!</div>
    </div>
  </div>
`
setTimeout(() => root`${app}`, 2000) 

But that feels like an anti-pattern, and will ultimately populate the DOM with an additional node anyway...

WebReflection commented 7 years ago

I just want to be able to upgrade an existing node tree against a template.

the literal template is the only way to map 1:1 nodes and create their updates, if you don't create nodes via template, there's no way to have such functionality if not by comparing somehow the template and the node and pretend these are the same thing, parsing their attributes and partial content, which might eventually need to be split in chunks of text nobody knows what could contain, etc.

TL;DR this is not supported, and most likely not needed since viperHTML is capable of injecting templates with listeners too so that whenever you need to update something, the very first time hyperHTML will replace its content.

It worked well with viper-news and I think it covers already 90% of use cases.

I have no idea if I should support such use case but it looks like an extension, rather than core, like the following could be:

hyperHTML.adopt(node)`
<div id="app" class="${'boom'}">
  <div class="${['static', 'dynamic'].join(' ')}">
    <button onclick="${(e) => console.log('clicked')}">Hello there!</div>
  </div>
</div>
`;
matthewrobb commented 7 years ago

That adopt method would work. I want to be able to control rendering of a custom element from within the element definition and do things like control my own attributes and classes etc.

WebReflection commented 7 years ago

if it'll ever happen in core, it won't be soon.

You can create an helper and play around though, after all templates are passed as variables, you can always use hyperHTML a part