Search code examples
vue.jsvuejs3v-modelsplitterprimevue

Make PrimeVue Splitter to have dynamic two-way panel size


I am using PrimeVue Splitter with Vue3 and composition API. I want to make the size of each panel dynamic , so when I click a button they return to their original size.

This is what I have so far

<button @click="fixPanels()" >fix</button>

<Splitter style="height: 100%" class="mb-3">
    <SplitterPanel :size=smallPanelSize >
        Panel 1  
    </SplitterPanel>
    <SplitterPanel :size=bigPanelSize >
        Panel 2 
    </SplitterPanel>
</Splitter>


export default {  
  setup(){

    let smallPanelSize = ref(30)
    let bigPanelSize = ref(70)  
    
    const fixPanels = ()=>{   
      message.value = 30;
      bigPanelSize.value = 70
    } 

    return{smallPanelSize, bigPanelSize, fixPanels}
    
  } 
} 

I can resize the panels freely, but when fixPanels is clicked, they dont return to their original size. I guess I have to use two-way binding somehow or v-model but I dont know how, there is no input here. If this is impossible, can you suggest a similar resizable panels component that the panel size is dynamically controlled?

Thank you


Solution

  • It seems that the SplitterPanel component does not handle reactive sizes. As a result, the prefix sizes are determined when the component is loaded and cannot be reset via reactivity.

    Therefore, the solution is to reload the components to force the sizes to be reset.

    For this, I created a Panels component which will be used only to display the panels and its reset button. The sizes will be retrieved via props and not assigned within the component. The resize button will send an event via emit in order to signal to the parent component the will to reload the component, in order to reset the sizes.

    The parent component will create the two default size variables with ref and send them to the props. The event will also be retrieved to call the fixPanels function. The only subtlety is to manage to force the reload of the Panels component. For this, I added a key value to the Panels component which will be incremented each time the fixPanels function is called. The purpose of this variable is only to make the child think that the values of the component have changed and to create the need to reload it.

    Just below, the two coded components that correspond to my explanations. (I used Vue 3 with the composition API and TypeScript, but you will probably know how to convert it to Vue 3 Option, if needed)

    Panels.vue

    <template>
      <button @click="$emit('reload')">fix</button>
    
      <Splitter style="height: 100%" class="mb-3">
        <SplitterPanel :size="smallPanelSize">
          Panel 1
        </SplitterPanel>
        <SplitterPanel :size="bigPanelSize">
          Panel 2
        </SplitterPanel>
      </Splitter>
    </template>
    
    <script setup lang="ts">
    
    import Splitter from "primevue/splitter";
    import SplitterPanel from "primevue/splitterpanel";
    
    const props = defineProps<{smallPanelSize: number, bigPanelSize:number}>();
    
    </script>
    

    ManagePanel.vue

    <script setup lang="ts">
    import { ref } from "vue";
    import Panels from "./components/Panels.vue";
    
    let small = ref(30);
    let big = ref(70);
    let componentKey = ref(0);
    
    function forceRender() {
      componentKey.value += 1;
    }
    
    function fixPanels() {
      small.value = 30;
      big.value = 70;
      forceRender();
    }
    </script>
    
    <template>
      <Panels
        :smallPanelSize="small"
        :bigPanelSize="big"
        :key="componentKey"
        @reload="fixPanels"
      />
    </template>