2
votes

I'm trying to make a simple multi-step form for my Rails 5 app using Vue.js. It's my first time using Vue so I am a bit stuck on how to make things work properly.

Right now I am trying to do the following:

  1. Click a Next button and set the li elements from step 1 to step N to have a class active.
  2. Click a Previous button and remove class active from step N.

Quite simple. Here's what I've got so far but I don't know where to go from here:

import Vue from 'vue/dist/vue.esm'

document.addEventListener('DOMContentLoaded', () => {
  Vue.component('step-item', {
    props: ['step'],
    template: '<li :class="{active: isActive}">{{ step.text }}</li>',
    data: function() {
      return {
        isActive: true // Right now I set them all to true
      }
    }
  })

  Vue.component('step-button', {
    props: ['name'],
    template: "<input :name='name' :value='name' @click='counter += 1' type='button' class='btn btn-secondary' />"
  })

  const progress = new Vue({
    el: '#vue-listing',
    data: {
      counter: 0,
      stepList: [
        {id: 0, text: 'Basics'},
        {id: 1, text: 'Location'},
        {id: 2, text: 'Images'},
        {id: 3, text: 'Other'}
      ]
    },
    methods: {
      addProgress: function() {return true}, // todo
      delProgress: function() {return true}  // todo
    }
  })
})

then I have my form

<div id="vue-listing">
  <!-- Display the progress through the form -->
  <ul class="text-center" id="listing-progressbar">
    <step-item v-for="item in stepList" :step="item" :key="item.id"></step-item>
  </ul>

  <fieldset>
    Step 1
    <step-button name="Next"></step-button>
  </fieldset>

  <fieldset>
    Step 2
    <step-button name="Previous"></step-button>
    <step-button name="Next"></step-button>
  </fieldset>

</div>

Right now I'm stuck on how to get the next and previous buttons to add or remove active from the current step. Eventually I will also need to hide and display the <fieldset> elements depending on the step.

Any help is much appreciated!

Thanks

1

1 Answers

1
votes

A simple implementation of this idea might look something like this.

console.clear()

document.addEventListener('DOMContentLoaded', () => {
  Vue.component('step-item', {
    props: ['step', 'active'],
    template: '<li :class="{active}">{{ step.text }}</li>',
  })

  const progress = new Vue({
    el: '#vue-listing',
    data: {
      activeStep: 0,
      stepList: [
        {id: 0, text: 'Basics'},
        {id: 1, text: 'Location'},
        {id: 2, text: 'Images'},
        {id: 3, text: 'Other'}
      ]
    },
    methods: {
      addProgress: function() {return true}, // todo
      delProgress: function() {return true},  // todo
    },
  })
})
.active {
  color: Blue
}
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
  <div id="vue-listing">
    <!-- Display the progress through the form -->
    <ul class="text-center" id="listing-progressbar">
      <step-item v-for="item, ndx in stepList" 
                 :step="item" 
                 :key="item.id"
                 :active="ndx === activeStep">
      </step-item>
    </ul>

    <fieldset v-if="activeStep === 0">
      Step 1
    </fieldset>

    <fieldset v-if="activeStep === 1">
      Step 2
    </fieldset>
    
    <fieldset v-if="activeStep === 2">
      Step 3
    </fieldset>
    
    <fieldset v-if="activeStep === 3">
      Step 4
    </fieldset>
    <hr>
    
  <button @click="activeStep--" :disabled="activeStep === 0">Prev</button>
  <button @click="activeStep++" :disabled="activeStep === stepList.length - 1">Next</button>
  </div>
</div>

I eliminated the step-button and moved the navigation out of the individual steps. Instead, there are now just buttons that will increment/decrement the current active step. Additionally the fieldsets will now show/hide as needed. Whether or not any individual step is active is passed through the active property.