Search code examples
vue.jsvuejs3primevue

How to control PrimeVue Panel state?


I'm using PrimeVue v3 and I have multiple Panels generated in a for loop. I also have buttons for Expand/Collapse All, which work until I manually expand or collapse one of the panels.

const panelCollapsed = ref(true);

function expandAll() {
  panelCollapsed.value = false;
}

function collapseAll() {
  panelCollapsed.value = true;
}

<Button aria-label="Expand All" @click="expandAll">Expand All</Button>

<Button aria-label="Collapse All" @click="collapseAll">Collapse All</Button>

<div v-for="myPanel in myPanelList">
    <Panel toggleable :collapsed="panelCollapsed"
      :pt="{ content: { style: 'padding: 0' }, root: { style: 'margin-bottom: 1rem;' } }" >
    </Panel>
</div>

How can I hook into the expanded/collapsed state of PrimeVue Panels?

I found something called PanelState in their documentation but there is no explanation on how to use it. To be honest I don't understand what PanelPassThroughMethodOptions are either, and I can't find anything related to it. I tried adding state to :pt like this: :pt="{ state: { d_collapsed: panelCollapsed } but that doesn't work.


Solution

  • There should be per component instance collapse states, not a single state. Expand/Collapse All won't work if they set panelCollapsed to the same value it currently has.

    Despite what the documentation states about collapsed prop:

    Defines the initial state of panel content

    The prop is reactive and updates the component state at any time. That the component also has update:collapsed means that it's intended to be used with v-model.

    const myPanelStates = ref([...]);
    
    function expandAll() {
      myPanelStates.value.fill(false);
    }
    
    function collapseAll() {
      myPanelStates.value.fill(true);
    }
    
    function updatePanel(value, index) {
      myPanelStates.value[index] = value
    }
    
    <Button @click="expandAll">Expand All</Button>
    
    <Button @click="collapseAll">Collapse All</Button>
    
    <div v-for="(myPanel, index) in myPanelList">
      <Panel
        :collapsed="myPanelStates[index]"
        @update:collapsed="updatePanel($event, index)"
    

    myPanelStates should have the same length as myPanelList and potentially be stored inside of it, this would allow to use v-model:collapsed="myPanel.state" instead of a prop and event handler.

    state refers to this kind of use, as it follows from TypeScript types in the documentation:

    <Panel
     :pt="{
       content: { style: 'padding: 0' },
       root(options) {
         options.state.d_collapsed = myPanelStates[index];
         return { style: 'margin-bottom: 1rem;' }
       }
     }"
    >
    

    The documentation doesn't state that state is intended for mutation, but at least it's possible because it's reactive. This would be helpful to extend the component if it didn't have collapsed property. In the current implementation of Panel the use of pt to access state serves the same purpose as accessing component instance directly like panelRef.value.d_collapsed, but this may change in future.