12

I am using a thrid party library to render certain elements in a 'container', and as per the documentation on lwc directives, I understand that I have to add a directive to specificaly indicate that the dom can me targeted by appendChild, however, when I try to add the directive to a non-empty element and deploy it, I get the following error:

"error":"LWC1086: Invalid contents for element with \"lwc:dom\". Element must be empty"

I have other embeded elements that are also used by the frameowrk (to do the same)

If i do not add the directive, I run into this error:

Error: [LWC error]: appendChild is disallowed in Element unless lwc:dom="manual" directive is used in the template.

this seems counterproductive =/ and i'm assuming there is no workaround ? (or is there?)

Not sure this is helpful, but here is an example snippet:

<template>
    <div class="slds-m-around_medium">
        <div id="someId" class="myInterface" > <!--lwc:dom="manual" Wont work -->
            <div class="tab-section">
                <a class="A-Tab" data-id="All" data-caption="All Content" lwc:dom="manual"></a>
            </div>

            <div class="main-section">
              <div class="results-column">

                <div class="resultList" data-layout="list" lwc:dom="manual">
                </div>

                <div class="pager" lwc:dom="manual"></div>
                <div class="resultsPerPage" lwc:dom="manual"></div>
              </div>
            </div>
          </div>
    </div>

.js file

--in my default class class that extends the LightningElement--
    renderedCallback() {
        if (this.framewokrInitialized ) {
            return;
        }
        this.framewokrInitialized = true;
        // initialize component
        Promise.all([
            loadScript(this, ResourcesC + '/js/resource1.min.js'),
            loadStyle(this, ResourcesC + '/css/resource2.css'),
            loadScript(this, ResourcesC + '/js/resource3.js')



        ]).then(() => {
                this.initMethod();
            }).catch(error => {
                //do something
            });     
        }

        initMethod(){

            Framework.aClass.aMethod('args', 'more args')
            const root = this.template.querySelector(".myInterface"); 
            Framework.init(root);
        }
4
  • 1
    Can you provide the snippet as where are you trying to add the directive?
    – Jayant Das
    Commented Mar 1, 2019 at 21:45
  • So you are trying to add the directive to the someId div. I don't think you will be able to do so, as the directive can be only applied on non-empty elements and that this is not the case here. Just a thought here - Couldn't you just add another empty div within it and utilize that here? Because in any case you are still adding a child element.
    – Jayant Das
    Commented Mar 1, 2019 at 22:01
  • no, since the framework looks for that specific div to append the elements, the directive can be only applied on --> empty elements. Add the lwc:dom="manual" directive to an empty native HTML element =(
    – glls
    Commented Mar 1, 2019 at 22:05
  • 1
    Hmm, I see. Not sure if you really have options here.
    – Jayant Das
    Commented Mar 1, 2019 at 22:38

2 Answers 2

12

The lwc:dom="manual" directive indicates to the engine that it should not touch the child nodes of a specific element. Because of this, the LWC compiler will complain if you add children elements to this.


The only way to add children node to an element with lwc:dom="manual" directive is via javascript:

<template>
  <div class="container" lwc:dom="manual"></div>
</template>
import { LightningElement } from 'lwc';

// The content you want to insert in container
const CONTAINER_HTML = `<div>Some HTML</div>`;

export default class extends LightningElement {
  renderCallback() {
    const container = this.template.querySelector('.container');
    container.innerHTML = CONTAINER_HTML;
    // ... Do some logic with the container ...
  }
}
2
  • 1
    This works but what if I want to append more HTML to the innerHTML without wiping out the values a user has entered into the form of the current innerHTML?
    – Arthlete
    Commented Mar 12, 2020 at 20:43
  • I get Cannot add property innerHTML, object is not extensible when trying this approach, I've outlined my problem in this question
    – Robs
    Commented Jun 17, 2020 at 14:26
0

Additional Details :: How to dynamically add content upon button click without clearing/wiping-out the existing content. Used a simple ul/li html elements and a button to demonstrate the concept. Comments are left to add insights.

<template>
    <lightning-card title="upquotient.com Coaching specialities" icon-name="custom:custom14">
        <div class="slds-var-m-around_medium">
            <button class="btn slds-button slds-button_brand">Add upquotient.com Differentiators</button>
            <div class="slds-var-m-top_medium">
                <!-- Error: [LWC error]: The `appendChild` method is available only on elements 
                that use the `lwc:dom="manual"` directive. 
                Fix :: add the directive lwc:dom="manual" as below-->
                <!-- Add the lwc:dom="manual" directive to an empty native HTML element. The owner of the 
                component calls appendChild() on that element to manually add the element -->
                <ul class="lstspecialities" lwc:dom="manual"></ul>
            </div>
        </div>
    </lightning-card>
</template>

import { LightningElement } from 'lwc';
export default class StkExchangeProblem extends LightningElement {

    ulEle = null
    renderedCallbackInvoked = false
    differentiators = ['Holistic coaching', 'Multiple MICRO Projects', 'Plethora of Workshops']

    renderedCallback(){
        if(!this.renderedCallbackInvoked){
            this.renderedCallbackInvoked = false
            this.ulEle = this.template.querySelector('.lstspecialities')
            this.template.querySelector('.btn').addEventListener('click', ()=>{
                this.manipulateLst()
            })
        }
    }

    manipulateLst(){
        let liEle = document.createElement('li')
        liEle.innerHTML = this.differentiators[ Math.floor(Math.random()*this.differentiators.length) ]
        this.ulEle.appendChild(liEle)
    }
}

You must log in to answer this question.

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