-
Notifications
You must be signed in to change notification settings - Fork 887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Render method doesn't work as spected #2189
Comments
You can either do it eagerly: import {html, render} from 'lit-html';
const handleClick = () => {
let content = document.getElementById('content');
render(html`New Content`, content);
}
render(html`
<div >
<button @click={handleClick}>Click me!</button>
<div id="content">
Old Content
</div>
</div>`, document.body);
// clear the container:
let content = document.getElementById('content');
content.innerHTML = ''; Or on-demand by keeping some state: import {html, render} from 'lit-html';
let containerCleared = false;
const handleClick = () => {
let content = document.getElementById('content');
if (!containerCleared) {
containerCleared = true;
content.innerHTML = '';
}
render(html`New Content`, content);
}
render(html`
<div >
<button @click={handleClick}>Click me!</button>
<div id="content">
Old Content
</div>
</div>`, document.body); |
Thank you for your comment, although it is not what I was looking for, it has helped me to better understand the problem. According to my idea, I think there should be an option such as: render(html``, root, {append:false}) |
@justinfagnani we can't really do it based on https://github.com/lit/lit/blob/main/packages/lit-html/src/lit-html.ts#L1145 this breaking change makes our website not working anymore the problem is we can only clean it once with innerHTML, otherwise, we constantly get the error. I think whether we add option to |
@gitawego you don't want to clear the container after rendering - and that's not what lit-html 1.x did - you just want to clear it before rendering. After that you can just re-render. |
We're not inclined to add or change behavior here, which is documented in the upgrade guide (https://lit.dev/docs/releases/upgrade/#lit-html). I think our suggestion would be to create a helper around |
Hey guys sorry to be a pain! I have inherited an older project using jquery. I am modernising the code. In particular this I always end up with an error if I try to render to a cleared element. This code: // Write TypeScript code!
const appDiv: HTMLElement = document.getElementById('app');
appDiv.innerHTML = `<h1>TypeScript Starter</h1>`;
// trying to replace jquery $(selector).html("<h1>lol</h1>");
appDiv.innerHTML = '';
render(html`<h1>lol</h1>`, appDiv);
appDiv.innerHTML = '';
render(html`<h1>lol</h1>`, appDiv); Or see my stackblitz I always get the following error: Error in /turbo_modules/lit-html@2.2.7/lit-html.js (93:55) Do you know what I am missing? 🧐 Thanks! |
This is really hard to use. There must be some way to check if the component needs to be cleared - rather than tracking our own state. Is there a property on the element to check if a render will work as expected? |
@robaho are you saying that you have to render to a container that has content and you can't clear it before rendering? I'm curious what creates those conditions. We thought it would be extremely rare. There is a property that |
Note: This comment relates to using Case 1: Only using
|
We are not using LitElement - this piece of code uses render(templateresult,element) directly - this is to keep the nodes in the DOM but it is also due to some legacy structuring. I had to create a method like: function rerender(id: string, template: TemplateResult | symbol) {
const elem = document.getElementById(id);
if (!elem) { console.log('not found ' + id); return; }
if (!renderOnce.has(id)) { renderOnce.add(id); elem.replaceChildren(); }
render(template, elem);
} which feels like a hack to work around a lit bug. Do you agree? To clarify - we use render() on a top-level component - then using rerender() on a child node that was generated by the original render. |
@robaho I get that you're not using LitElement, my question is more about what's requiring you to rendering into a container that has preexisting content that you want to clear. fwiw, a wrapper over import {render as litRender} from 'lit';
/*
* Clears the container on the first render. Must render into the whole container, renderBefore is not supported
*/
export const render = (v: unknown, container: HTMLElement | DocumentFragment, options?: RenderOptions) => {
if (options?.renderBefore !== undefined) {
throw new Error('renderBefore option not supported');
}
if ((container as any)['_$litPart$'] === undefined) {
// first render
container.replaceChildren();
}
return litRender(v, container, options);
}; You could also keep your own state for tracking the first render. 2.x just has different behavior here than 1.x - we had a lot of feature requests on 1.x to not clear the container, and very few observed cases where container clearing was required. So that's why I'm asking about your specific case here. What rendered the existing content that you want to clear? |
We have complex apps that have 10's thousands of cells. The cells can be grouped into sections. The sections can be collapsed - we don't use 'display: none' because the listeners will still fire - so when the section is collapsed we remove the section from the DOM. We do this by removing all of the child elements of the section. If the user then re-enables the section we need to render the template back into the section (parent element). The sections are all rendered from the intial render of the top-level template. I think the use-case is valid but I am open to other ways of doing this. (I think the same code you provided that checks for the existing of the field is better - thanks). I guess we could rerender the entire DOM using the top-level template but that seems way more inefficient. |
That's not necessarily true – depending on your use-case. As I said in #2189 (comment), you can re-render the top level template to the same container changing nothing, and no DOM updates will be performed. There may be a performance problem if you have 10 thousand dynamic bindings, but even then there are tools that can be used. E.g. if you are rendering long lists, the You could also use the You can check out how lit-html works under the hood if you're interested as well. What I described above is the internal render phase called the |
The entire DOM consists of 270k nodes and a section is typically 1/6 of that. |
@robaho I think we should take this to a new issue. What you really want here is multiple coordinated render roots, without components (I would recommend using components for this though - they coordinate this kind of incremental/isolating re-rendering quite well!). The old If you for some reason can't factor the sections as components, then what you want here is a want to have multiple render roots in a tree. I've considered this before, and it would be possible with a directive that calls Something like this maybe: // This creates a new object that manages independently renderable roots
const roots = new RenderRootGroup();
// Render the top-level root of the page
render(html`
<h1>Root</h1>
<!-- roots.render() performs a new render() call and stores the part by the key given -->
${repeat(sections, (s) => s.id, (section) => roots.render(section.id, renderSection(section)))}
`, document.body);
// Called to update a specific section
const updateSection = (section) => roots.update(section.id, , renderSection(section));
// Just the template for the section
const renderSection = (section) => html`
<h2>Section: ${section.name}</h2>
`; |
I don't think we're on the same page. I think render() shouldwork as documented. If I clear the children, and call render(template,element) - it crashes. This is a bug to me. The fact that it fails because Lit is attempting to optimize behind the scenes has no bearing imo - the method is not working as documented. If it is easier for Lit to add a method renderReplace() that would be acceptable as well, and document the other that you can ONLY use render() to affect the DOM tree for a container element that was rendered by a previous call to render(). |
Description
render method not overwritting the existing HTML
Steps to Reproduce
Expected Results
On click, doesn't overwrite the content.
Actual Results
The text was updated successfully, but these errors were encountered: