4

I am facing a problem in the following code in my lwc component.

Whenever I try to append a child dynamically from Js in LWC component. I got this error.

HTML :

<template>
    <lightning-combobox
        name="comboType" value={comboboxValues}
        label="label" onchange={handleChange}
        placeholder="nnnn" class="margin-bottom-8"
        options={Options}
        required>
    </lightning-combobox>

    <div data-id="overview" lwc:dom="manual"></div>
</template>

JS :

    handleChange(event){

        var element = this.template.querySelector('[data-id="overview"]');

        child = '<c-lwc_simulation></c-lwc_simulation>';

        element.appendChild(child);

    }

enter image description here

1
  • Hi. You forgot to specify the error.
    – Phil W
    Commented Mar 5, 2020 at 8:43

3 Answers 3

4

It was not possible to dynamically create LWC components from JavaScript (see update later). They can only be created within templates. You need to change your code to either leverage an iteration over some data held in the parent, like:

<template for:each={simulation} for:item="sim">
    <c-lwc_simulation key={sim.key}></c-lwc_simulation>
</template>

... or to consider using Aura (which does support dynamic component creation), or to consider some other approach (like having multiple different templates to select from).

UPDATE 2024: Dynamic creation in LWC

Yes, this is now a thing as long as you have Lightning Web Security enabled in your org. This has performance implications and can add complexity around passing data to the component, so should be used with caution.

See the documentation for more details.

2

It could very well be that you don't need dynamic component creation. While this is dynamic DOM creation is widely used, and probably the default behavior of a lot of web developers when creating UI features in JS, the design of LWC and the idiomatic approach to creating LWC is to create conditionally rendered elements.

There is another post in this site where Deigo Val (one of the architects of LWC) explains the approach that was taken with the framework and why. But I think it can be summed up by this one question that he asks in his answer:

Why would you want to...dynamically create a very simple leaf component?

Now in your case, I don't know what <c-lwc-simulation> is meant to be, or if it will be a "simple leaf component" as he surmises, but the architecture of creating static templates with conditional rendering is what we have to work with today. (Note, Diego does say that there may be some limited conditional rendering in the future, but no word on when just yet, last I heard.)

Reading between the lines a bit, it looks like you're going for the logic "if something is selected from the <lightning-combobox> then render something else". Going on that assumption, you need to wrap your <c-lwc-simulation> in a conditionally rendered template tag and have some data in the child LWC change the rendering based on that. I'm not certain what final outcome you're going for, but one way this could be done is as follows:

// controller code
// add a property to make rendering conditional
showLWCSimulation = false; 

...

// modify the handleChange function to flip the boolean property
handleChange(event){

    this.comboboxValues = event.target.value; 

    ...
    // perform some other logic here with the data, perhaps
    ...

    //new code
    if (this.comboboxValues === 'My Selected Value') {
        this.showLWCSimulation = true;
    }
}

If you need <c-lwc-simulation> to respond to the selected value, then create an @api enabled property to pass in some data so that you can make it respond:

import { LightningElement, api > from 'lwc'; 

export default class LwcSimulation extends LightningElement {

    @api passedInValue; 

    // other properties and functions here

}

Then in the template, something like this:

<template>
    ...
    <!-- static conditional placement of c-lwc-simulation -->
    <template if:true={showLWCSimulation}>
        <c-lwc-simulation passed-in-value={comboboxValues}></c-lwc-simulation>
    </template>
</>

Incidentally, there are uses for lwc:dom="manual", and that does work with third party libs. Why it doesn't work here, I suspect it's because you're attempting to add a custom element (which is locked down, outside the framework), versus a standard element (which is what you'd find in a third party lib).

Long story short, I think you may benefit from taking some time and understand the standard use patterns of LWC. If you'd like a bunch of examples, check out the LWC Recipes app on github.

4
  • In my parent LWC component I have to show only one child based on the value the selected value in the lightning-combobox. I've been using the same approach of what you gave as an example. but since I have more than 50 child it's pretty heavy to have to deal with more than 50 variable just to be able to know which child to make appear. that's why I was looking for a way to do in dynamic way. Commented Mar 5, 2020 at 10:26
  • I agree...with that kind of variability, even allowing dynamic templates could be a lot of different artifacts to manage in the project. Without knowing more details, the only suggestion I can offer is to think about what part of the 50 different use cases can reused. Try to encapsulate as much of that as you can in the root child component.
    – pchittum
    Commented Mar 5, 2020 at 10:45
  • Thank you for the explanation. Commented Mar 5, 2020 at 11:26
  • Sure thing. And if you have a better solution, don't hesitate to come back and provide your own answer. :-)
    – pchittum
    Commented Mar 5, 2020 at 11:27
2

With Winter '24 it is now possible to create a child LWC dynamically from parent LWC, see: Dynamically Instantiate Components

To instantiate a component dynamically, use the <lwc:component> managed element with the lwc:is directive in a component's HTML file.

<template>
    <div class="container">
        <lwc:component lwc:is={componentConstructor}></lwc:component>
    </div>
</template>

<lwc:component> serves as a placeholder in the DOM that renders the specified dynamic component. You must use <lwc:component> with the lwc:is directive.

The lwc:is directive provides an imported constructor at runtime to the <lwc:component> managed element. lwc:is accepts an expression that resolves to a LightningElement constructor at runtime.

In the component's JavaScript file, import the custom element using the import() dynamic import syntax.

import { LightningElement } from "lwc";
export default class extends LightningElement {
  componentConstructor;
  // Use connectedCallback() on the dynamic component
  // to signal when it's attached to the DOM
  connectedCallback() {
    import("c/concreteComponent")
      .then(({ default: ctor }) => (this.componentConstructor = ctor))
      .catch((err) => console.log("Error importing component"));
  }
}

The import() call returns a promise that resolves to a LightningElement constructor. The element is then rendered instead of the lwc:component placeholder. The tag name used for the dynamic component is the value that's returned for the given constructor.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .