Episerver项目中Vue.js嵌套手风琴类绑定失效问题求助
Let's break down why your isNested class isn't applying correctly, and walk through the best fixes to get that indent working as expected.
The Core Issue
When you call isNested() directly in the template's v-bind:class, Vue only runs this method once during initial render. If your nested accordions load after that (like during server-side hydration, or when dynamic content renders), the method won't re-run to detect the updated DOM structure. When you put the logic in the click handler, it runs after the DOM is fully ready, which is why it worked there.
Solution 1: Use Reactive State + Lifecycle Hooks (Most Reliable)
This approach aligns with Vue's reactive paradigm—we'll calculate the nested state after the component mounts (and re-check if content updates dynamically):
Step 1: Update Your Vue Component
<script> import componentMounted from '../mixins/component-mounted'; export default { mixins: [componentMounted], data() { return { isNestedState: false, // Store server-rendered values in reactive data for easy access collapseId: '@collapseId', targetClass: 'collapsible-list-item-block' }; }, mounted() { // Check nesting after component mounts this.checkNestedStatus(); }, updated() { // Re-check if dynamic content (like nested accordions) loads later this.checkNestedStatus(); }, methods: { collapsibleClicked(id) { $(window).trigger("collapseClicked", [id]); }, checkNestedStatus() { const element = document.getElementById(this.collapseId); if (!element) { this.isNestedState = false; return; } let parent = element.parentElement; // Traverse up the DOM properly (no hardcoded parent jumps) while (parent) { if (parent.classList?.contains(this.targetClass)) { this.isNestedState = true; return; } parent = parent.parentElement; } this.isNestedState = false; } } } </script>
Step 2: Update the CSHTML Template
Bind the class to the reactive state variable instead of calling the method directly:
<div v-bind:class="{isNested: isNestedState}" class="collapsible-list-item-block grid @Model.GetHiddenPrintClassName">
Solution 2: Component Prop Approach (Cleanest for Reusable Components)
If your accordions are set up as reusable Vue components, skip DOM traversal entirely and pass a isNested prop from parent to child. This is far more maintainable:
Step 1: Add a Prop to the Vue Component
<script> import componentMounted from '../mixins/component-mounted'; export default { mixins: [componentMounted], props: { isNested: { type: Boolean, default: false } }, methods: { collapsibleClicked(id) { $(window).trigger("collapseClicked", [id]); } } } </script>
Step 2: Pass the Prop in CSHTML
When rendering an accordion inside another accordion's content, explicitly set :is-nested="true":
<!-- Parent accordion --> <generic-component inline-template class="vue-block-app" server-rendered="true"> <!-- ... parent accordion markup ... --> <b-collapse id="@collapseId" @collapsedByDefault class="collapsible-list-item-block__content" style="display: none;"> <!-- Nested child accordion --> <generic-component inline-template class="vue-block-app" server-rendered="true" :is-nested="true"> <!-- ... child accordion markup ... --> </generic-component> </b-collapse> </generic-component>
Step 3: Bind the Class in Template
<div v-bind:class="{isNested: isNested}" class="collapsible-list-item-block grid @Model.GetHiddenPrintClassName">
Quick Fix: Correct DOM Traversal Logic
If you want to stick with the original method-based approach, fix the hardcoded parentElement.parentElement jump—this might be skipping the actual parent accordion element. Replace the traversal logic in your isNested method with:
isNested(id, className) { let element = document.getElementById(id); if (!element) return false; let parent = element.parentElement; while (parent) { if (parent.classList?.contains(className)) { return true; } parent = parent.parentElement; } return false; }
Note: This still has the limitation of only running once on initial render, so it won't work for dynamically loaded nested accordions.
内容的提问来源于stack exchange,提问作者FilipP

