Skip to content

DOM content inside a <template if:true={condition}>...</template> is not cleared... #1028

Open
@jye-sf

Description

@jye-sf

Description

DOM content inside an if:true directive is not cleared when a specific set of directives are nested inside. Two nested if:true directives work fine, but adding a for:each directive inside the inner if:true directive breaks the top-most if:true.

<!-- cleared properly -->
<template>
    <template if:true={hasValue}>
        <template if:true={nestedValue}>
            <div class="slds-p-around_x-large">
                <lightning-button label='Toggle HasValue' onclick={toggleHasValue}>
                </lightning-button>
            </div>
        </template>
    </template>
</template>
<!-- not cleared -->
<template>
    <template if:true={hasValue}>
        <template if:true={nestedValue}>
            <template for:each={addressLines} for:item="line">
                <div key={line} class="slds-truncate">
                    {line}
                </div>
            </template>
            <div class="slds-p-around_x-large">
                <lightning-button label='Toggle HasValue' onclick={toggleHasValue}>
                </lightning-button>
            </div>
        </template>
    </template>
</template>

Steps to Reproduce

See playground link for steps to reproduce.

https://playground.lwcjs.org/projects/f0-19z5M6/2/edit

Expected Results

DOM content is properly cleared when condition is false.
DOM content only appears once when condition is true.

Actual Results

DOM content is not cleared when condition is false
DOM content duplicate is appended when condition is true

Browsers Affected

Tested on Chrome

Version

  • LWC: 0.34.3

Possible Solution
I couldn't identify whether this was an engine or a compiler issue, but here's what's happening under the covers:

In the working scenario, the template is compiled as

return [$cmp.hasValue ? $cmp.nestedValue ? api_element("div", {
      classMap: {
        "slds-p-around_x-large": true
      },
      key: 4
    }, [api_custom_element("lightning-button", button, {
      props: {
        "label": "Toggle HasValue"
      },
      key: 5,
      on: {
        "click": _m0 || ($ctx._m0 = api_bind($cmp.toggleHasValue))
      }
    }, [])]) : null : null];

In the broken scenario, the template is compiled as

return $cmp.hasValue ? $cmp.nestedValue ? api_flatten([api_iterator($cmp.addressLines, function (line) {
      return api_element("div", {
        classMap: {
          "slds-truncate": true
        },
        key: api_key(5, line)
      }, [api_dynamic(line)]);
    }), api_element("div", {
      classMap: {
        "slds-p-around_x-large": true
      },
      key: 6
    }, [api_custom_element("lightning-button", button, {
      props: {
        "label": "Toggle HasValue"
      },
      key: 7,
      on: {
        "click": _m0 || ($ctx._m0 = api_bind($cmp.toggleHasValue))
      }
    }, [])])]) : [] : [];

The main difference here is that in the working scenario, the new template is returned as [null] while in the broken scenario, they're returned as [].

The children are considered static because they aren't created by an iteration:

snabbdom function then fails to update the children because it assumes the array lengths should be the same

Additional context/Screenshots
This is the root cause of a high priority bug with a customer case in 218/patch. If a fix isn't easy/quick, any suggestions on a workaround we could implement on our component would be extremely helpful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions