Description
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:
-
(edited: previously pointed to wrong function in hooks.ts)
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