Lit-html: Moving a uniquely identified element between containers

Created on 25 Jul 2018  路  5Comments  路  Source: Polymer/lit-html

Hello!

Is there any way to uniquely identify and move an element between containers in a render? Here's an example of what I mean. There are two lists of the but same data but filtered in different ways. One element is marked in red at the beginning to track its position / reuse. After 1 second the data is modified such that the red item now belongs in the second list and everything is rerendered. However, instead of being moved the item is removed from the first list and recreated in the second, causing it to no longer be red:

import { html, render } from 'https://unpkg.com/[email protected]/lit-html.js';
import { repeat } from 'https://unpkg.com/[email protected]/lib/repeat.js';

var params = {

    items: [{id:1, value: 1} , {id:2, value:2}, {id:3, value: 3}, {id:4, value: 'word'}, {id:5, value:4}]

}

var rerender = () => {

    render(html`
        <h4>Numbers</h4>
        <ul>
            ${
                repeat(
                    params.items.filter(o => !isNaN(o.value)),
                    o => o.id,
                    o => html`<li id="${ o.id }">value: ${ o.value }</li>`
                )
            }
        </ul>

        <h4>Strings</h4>
        <ul>
            ${
                repeat(
                    params.items.filter(o => isNaN(o.value)),
                    o => o.id,
                    o => html`<li id="${ o.id }">value: ${ o.value }</li>`
                )
            }
        </ul>
    `, document.body);

}

rerender();
document.getElementById(params.items[0].id).style.background = 'red'

setTimeout(() => {

    params.items[0].value = 'word2';
    rerender();

}, 1000);

In this case the impact of deletion and recreation is trivial but there are use cases where moving the element is desired, especially in cases where the element being recreated is complex.

Is this possible at the moment?

Thanks!

Medium Enhancement Enable new use cases

All 5 comments

It should be possible to write a directive to do this. The directive will need a cache of nodes, and share that cache if nodes are intended to be moved between directive call sites.

Thanks! It would be nice to have a few more examples of how to write more complex directives.

In this case it seems like the repeat function could be changed to take a shared cache Map as a fourth argument so they could share the created parts. In that case the cleaning of the map would need to be removed, as well. Is there are reason a WeakMap isn't being used instead? Then the map wouldn't need to be manually cleaned. _Edit: Just remembered that WeakMap keys need to be objects. I suppose cleaning would need to be relegated to the user, as well? Maybe there's a cleaner solution._

Here's how it might be used:

    const sharedNodeCache = new WeakMap();

    // ...

    render(html`
        <h4>Numbers</h4>
        <ul>
            ${
                repeat(
                    params.items.filter(o => !isNaN(o.value)),
                    o => o.id,
                    o => html`<li id="${ o.id }">value: ${ o.value }</li>`,
                    sharedNodeCache
                )
            }
        </ul>

        <h4>Strings</h4>
        <ul>
            ${
                repeat(
                    params.items.filter(o => isNaN(o.value)),
                    o => o.id,
                    o => html`<li id="${ o.id }">value: ${ o.value }</li>`,
                    sharedNodeCache
                )
            }
        </ul>
    `, document.body);

Of course you run into issues if both of the repeats try to use the same key, but that's a problem in the case of a single repeat, as well.

I was able to get this working by modifying the repeat function while retaining the previous functionality of repeat. It really just took changing a couple lines and adding a new function signature. One caveat, as I mentioned before, is that the provided map must be manually cleaned by the containing application once the relevant rendering has happened. I can make a PR with the changes if you're interested in seeing the differences and behavior.

Would you consider including a behavior like this in the library either as a variant of repeat or something that extends it? Or at least making the repeat directive extendeable so this can be easily implemented? It only makes sense to take advantage of an optimized repeat functionality already built into the library if you want do this.

Thanks!

@gkjohnson thanks for the feedback! @justinfagnani a PR like this should be welcomed I would think?

I hope the idea here is not to just sit on this lib while focusing on Polymer. With 12 PRs in limbo, and no responses to the above, it sure seems like it. :(

What are the plans for lit-html, is it going to be just rolled into Polymer somehow or will it stand on its own and be enhanced as it should?

Thanks @aadamsx I've added a PR here https://github.com/Polymer/lit-html/pull/421!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cbelden picture cbelden  路  4Comments

dakom picture dakom  路  4Comments

Christian24 picture Christian24  路  4Comments

depeele picture depeele  路  3Comments

justinfagnani picture justinfagnani  路  3Comments