Home » vue » Why doesn't v-show displayed when I go back to the previous step?

Why doesn't v-show displayed when I go back to the previous step?

Posted by: admin November 26, 2021 Leave a comment

Questions:

I have an SAP in VUE that consists of a form in 4 steps.
When submit in the first step button and redirect to second step, if I return to first step the last component is stopped showing, in spite of being shown before changing the component.
this is my template

   <input
      type="text"
      id="animalName"
      ref="animalName"
      v-model="animalFormInfo.name"
      @keypress="onChangeName($event)"
      @keyup="onChangeName($event)"
      />
      <div class="loader-spinner" v-if="loading">
        <app-loader>
      </div>
    </div>
  </div>
</div>

<div v-show="isNameCompleted">
  <div class="from-group">
    <h1 class="title">
      For
      <span>{{this.formInfo.name}}</span>, you have:
    </h1>
    <div class="list-services">
      <div
        class="column-service"
        v-for="estimation in filteredEstimation"
        v-bind:key="estimation.name"
      >
        <div>{{estimation.name}}</div>
        <div>{{estimation.description}}</div>
        <div>{{estimation.price.toString().replace(".", ",")}}</div>
      </div>
    </div>
  </div>
</div>
<div class="button-line" v-show="isLoaderFinished"></div>
<div class="forward" v-show="isLoaderFinished">
  <div class="forward-button">
    <button
      ref="submit"
      v-bind:disabled="!isFormCompleted"
      type="submit"
      @click="navigateToCreate"
    >
      SEND INFO
    </button>
  </div>
</div>

and below the data and the method in which I control the load of the components


data() {
  return {
    step: 1,
    isFormCompleted: false,
    isBreedSelected: false,
    isNameCompleted: false,
    loading: false,
    isLoaderFinished: false,
    myTimeout: 0
  };
},
methods:{
  onChangeName(event) {
    if (this.myTimeout >= 0 && event) {
      clearTimeout(this.myTimeout);
      this.loading = true;
      this.isNameCompleted = false;
    }
    this.myTimeout = window.setTimeout(() => {
      this.isNameCompleted = true;
      const typedAnimalName = event.target._value;
      this.capitalizeAnimalName(typedAnimalName);
      this.loading = false;
      this.isLoaderFinished = true;
      this.isFormCompleted = true;
      setTimeout(() => (this.$refs.scrolled_3.focus()), 10);
    }, 2200);
  },

  capitalizeAnimalName(typedAnimalName) {
    var splitAnimalName = typedAnimalName.toLowerCase().split(' ');
    for (var i = 0; i < splitAnimalName.length; i++) {
      splitAnimalName[i] = splitAnimalName[i].charAt(0).toUpperCase() + splitAnimalName[i].substring(1);
    } 
    return this.animalName = splitAnimalName.join(' ');
  },
},

Once I reach step 2 and return to step 1 only the info is shown until the input with id "animalName".
What am I doing wrong?
Is there any reason why when loading the first component it follows the logic and shows them and once I go to the next one they disappear?

thank you all for your time and help in advance

Answers:

The problem is that the state of the component is not persistent.

There are multiple solutions for this.

1. Manage state globally with Vuex

This is probably the default, or most common way of handling this kind of situation. Instead of storing the data in the component, you use this library to manage the state. Most likely you can just implement vuex in your project and not read on. It’s a solid choice with too many upsides to list.

2. Encapsulate form state in the parent component.

Instead of managing the state within the step component, you can create the state for the relevant data inside the parent object.

example:

<Step2
  v-if="step === 2"
  :formData="formData"
  :updateFormData="updateFormData"
  :nextStep="() => gotoStep(3)"
></Step2>

The idea is that the Step2 component would have the data passed in through formData prop. And any time that data changes, updateFormData function is called with the new data. You could even simplify it by utilizing the v-model.

3. DIY store

If you’re using Vue js 2 (>= 2.6, < 3), you can do this with observable

Vue 2.6+

create a store.js file

import Vue from "vue";
export const store = Vue.observable({
  step: 1,
  isFormCompleted: false,
  isBreedSelected: false,
  isNameCompleted: false,
  loading: false,
  isLoaderFinished: false,
});

Then you can call your store.js from any component, and if you assign it to this in the beforeCreate hook, you can use it just like data (this.store.isFormCompleted in script, store.isFormCompleted in template)

import { store} from 'store.js';
//...
  beforeCreate(){
    this.store = store;
  }

if you want to skip the extra store., you could even destructure the store object.

Vue 3

Similar to the other one there, but use a reactive() (or multiple ref()). Let me know if you want me to expand on this.

###

If each step is its own component, you can use the keep-alive component to cache the components and their state, so that when you switch between them, their state is retained. I would personally prefer that over using Vuex in this case, since it’s much simpler.

<keep-alive>
    <step-1-component v-if="step === 1"></step-1-component>
    <step-2-component v-if="step === 2"></step-2-component>
    <step-3-component v-if="step === 3"></step-3-component>
</keep-alive>

See: https://vuejs.org/v2/api/#keep-alive

###

It is so because the component is re-rendering when you go back to the first page, so all the default states will take effect. Try saving your states with Vuex so that all components will be controlled by the state from the store.