Search code examples
javascripttypescriptvue.jsprimevue

How to do script actions before a Tailwind PrimeVue v3 Stepper panel change


I'm trying to use the Tailwind PrimeVue v3 Stepper component and I need to perform some actions before moving between each StepperPanel.

For example, I want to perform a simple console.log("before panel change") from inside the <script setup lang='ts'> area before each panel changes.

How can that be done?

From the docs I can see that the PrimeVue Stepper component has update:activeStep and step-change emits but those are triggered after the panels change. And I need to do the script actions before the panel changes.

The docs also show the use of prevCallback and nextCallback for moving between panels, so I assume I need to hook into those somehow. But those are provided inside the template and I have no idea how to access them inside the script tag area.

Here is a live URL to try with: https://stackblitz.com/edit/nuxt-starter-k97nc8?file=app.vue

<template>
  <div class="card flex justify-center">
    <Stepper>
      <StepperPanel header="Header I">
        <template #content="{ nextCallback }">
          <div class="flex flex-col h-[12rem]">
            <div
              class="border-2 border-dashed border-surface-200 dark:border-surface-700 rounded-md bg-surface-0 dark:bg-surface-950 flex-auto flex justify-center items-center font-medium"
            >
              Content I
            </div>
          </div>
          <div class="flex pt-4 justify-end">
            <Button
              label="Next"
              icon="pi pi-arrow-right"
              iconPos="right"
              @click="nextCallback"
            />
          </div>
        </template>
      </StepperPanel>
      <StepperPanel header="Header II">
        <template #content="{ prevCallback, nextCallback }">
          <div class="flex flex-col h-[12rem]">
            <div
              class="border-2 border-dashed border-surface-200 dark:border-surface-700 rounded-md bg-surface-0 dark:bg-surface-950 flex-auto flex justify-center items-center font-medium"
            >
              Content II
            </div>
          </div>
          <div class="flex pt-4 justify-between">
            <Button
              label="Back"
              severity="secondary"
              icon="pi pi-arrow-left"
              @click="prevCallback"
            />
            <Button
              label="Next"
              icon="pi pi-arrow-right"
              iconPos="right"
              @click="nextCallback"
            />
          </div>
        </template>
      </StepperPanel>
      <StepperPanel header="Header III">
        <template #content="{ prevCallback }">
          <div class="flex flex-col h-[12rem]">
            <div
              class="border-2 border-dashed border-surface-200 dark:border-surface-700 rounded-md bg-surface-0 dark:bg-surface-950 flex-auto flex justify-center items-center font-medium"
            >
              Content III
            </div>
          </div>
          <div class="flex pt-4 justify-start">
            <Button
              label="Back"
              severity="secondary"
              icon="pi pi-arrow-left"
              @click="prevCallback"
            />
          </div>
        </template>
      </StepperPanel>
    </Stepper>
  </div>
</template>

<script setup lang="ts">
// Do something in here BEFORE moving between stepper panels
// For example, do a console.log("before panel change")
// I assume I need to use nextCallback and prevCallback from the template
// But I don't know how to access those callbacks here inside script
</script>

<style scoped>
.p-stepper {
  flex-basis: 50rem;
}
</style>

Solution

  • This is reasonably common situation, and a possible implication for such a requirement could be that an action is asynchronous; this couldn't be handled with Stepper events because Vue events don't have a mechanism to wait for asynchronous side effect in event handlers.

    Third-party libraries aren't generally expected to cover this range of requirements, they need to provide enough flexibility through callbacks; this what Stepper already did here. Since step action is triggered in the same component "before" action needs to be performed, a developer has full control over their execution:

    <template #content="{ nextCallback }">
        ...
        <Button
          @click="changeStep($event, nextCallback)"
        />
      </div>
      ...
    
    <script setup lang="ts">
    function changeStep(event: MouseEvent, callback: (event: Event) => void) {
      // Do some actions
      callback(event);
    }
    </script>
    

    Notice that original mouse event is passed as is in case the implementation depends on it.