2

I am using vuejs-wizard to create registration page, I have each tab in component like this

<form-wizard color="#fcab1a" title="" subtitle="" finish-button-text="Register">
  <tab-content title="Personal Info" icon="icon-location3 fa-2x">
    <personal-info></personal-info>
  </tab-content>
  <tab-content title="Contact Info" icon="icon-box fa-2x">
    <contact-info></contact-info>
  </tab-content>
  <tab-content title="Address" icon="icon-alarm fa-2x">
    <address></address>
  </tab-content>
</form-wizard>

personal info:

<template>
  <div class="card">
    <div class="card-header">
      <h5 class="card-title">Personal Info</h5>
    </div>
    <div class="card-body">
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Full Name <span class="text-danger">*</span></label>
            <input type="text" value="" class="form-control" v-model="name" />
          </div>
        </div>
      </div>
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Age <span class="text-danger">*</span></label>
            <input type="number" value="" class="form-control" v-model="age" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

contact info:

<template>
  <div class="card">
    <div class="card-header">
      <h5 class="card-title">Contact Info</h5>
    </div>
    <div class="card-body">
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Mobile <span class="text-danger">*</span></label>
            <input type="text" value="" class="form-control" v-model="mobile" />
          </div>
        </div>
      </div>
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Email <span class="text-danger">*</span></label>
            <input
              type="number"
              value=""
              class="form-control"
              v-model="email"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

so my question is, what is the best way to submit the form, do I need vuex to store the state or is there a better/easier way ?

Thanks

Daniel
  • 34,125
  • 17
  • 102
  • 150
Peril
  • 1,569
  • 6
  • 25
  • 42
  • If in case you are having your save button inside the parent component, You can pass an single object whose keys would be used as v-model in various components input field. Since objects are passed by reference your entire data would be available in parent component. – Himanshu Apr 30 '20 at 22:10

3 Answers3

1

It depends...

The answer depends on various factors. For example, are you using vuex already, size of the app, test-ability of app, and even how are fields get validated (asynch/api validations?).

When it's a simple app, and I only have direct parent=>child relationships, I tend to skip adding the Vuex as a dependency. (but I mostly deal with SPAs, so I usually use it) YMMV.

In this case, that would require that the fields are defined in the parent. Then adding props and emitters for each value to the children, and a listener on the parent. As you start to add more fields though, you might find this tedious, and opt to pass fields in an object either in groups, or as whole (and only pick the ones you need in tab), and then you can implement your own v-model in the tab components which can make it pretty easy to pass the object around

If you're using a more recent Vue version (2.6+), you could use vue.observable to share a store between multiple components without the bells/whistles of Vuex

There's a good article that shows how to build a vuex clone with it, but in reality it's much, much simpler than that to create a store that would suit your needs. Let me know in the comments if you're interested in how to implement it, and I can describe it.


Rolling with custom store

it's really as simple as this

Create a store.js file with...

import Vue from 'vue';

const store = Vue.observable({
  name: null,
  email: null,
  age: null,
  mobile: null,
});

export default store;

then in any component that you want to have access to it, add the store during create

import store from "../store";

export default {
  name: "PersonalInfo",
  created() {
    this.$store = store;
  }
};

now the all the store is available to you in the template through $store

<input type="text" value class="form-control" v-model="$store.name">

codesandbox example

You lose the benefits, such as "time-traveling" over mutations that Vuex offers. Because you're not dealing with the scale that the flux pattern (vuex) was meant for, I would use this solution in an app this size.

Daniel
  • 34,125
  • 17
  • 102
  • 150
  • Thank you Daniel, I am really interested to avoid vuex, I don't use it in my app, I just have simple wizard and each tab in separate component and want to send the data, could you please describe the way to go with the solution you suggest – Peril May 01 '20 at 11:32
  • updated answer with observable solution and example – Daniel May 01 '20 at 15:07
  • Thank you Daniel, I will check that, also I need to figure out how to add the validation, I am using vuelidate – Peril May 02 '20 at 10:25
  • You can treat the data in the store (vue.observable) the same way as data, so use of vuelidate or any validation library should be same. – Daniel May 03 '20 at 04:33
0

Ya you should just use vuex, it combines like data so that it isn't spread out across multiple files. If you are going to be sending this information back to your backend, it makes it easier to have most backend connections in one place. without the wizard thing I redid your code with a store like this. Note the computed property instead of using data. By doing it as a computed property you don't have to write any code to change the variables stored inside of the store.

Personal.vue

<template>
  <div class="card">
    <div class="card-header">
      <h5 class="card-title">Personal Info</h5>
    </div>
    <div class="card-body">
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Full Name <span class="text-danger">*</span></label>
            <input
              type="text"
              value=""
              class="form-control"
              v-model="register.name"
            />
          </div>
        </div>
      </div>
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Age <span class="text-danger">*</span></label>
            <input
              type="number"
              value=""
              class="form-control"
              v-model="register.age"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    register() {
      return this.$store.state.register;
    },
  },
};
</script>

Contact.vue

<template>
  <div class="card">
    <div class="card-header">
      <h5 class="card-title">Contact Info</h5>
    </div>
    <div class="card-body">
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Mobile <span class="text-danger">*</span></label>
            <input
              type="text"
              value=""
              class="form-control"
              v-model="register.mobile"
            />
          </div>
        </div>
      </div>
      <div class="form-group">
        <div class="row">
          <div class="col-md-6">
            <label>Email <span class="text-danger">*</span></label>
            <input
              type="number"
              value=""
              class="form-control"
              v-model="register.email"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  computed: {
    register() {
      return this.$store.state.register;
    },
  },
  methods: {
    submit() {
      this.$store.dispatch("register", {
        person: this.register,
      });
    },
  },
};
</script>

<style></style>

store/index.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    register: {},
  },
  actions: {
    // register({commit}, data){
    //put in some stuff here.
    //},
  },
});

If you decide to go the store route, all you have to do is this 1. npm install vuex 2. add a folder inside of your src folder called store 3. add a file named index.js 4. go to main.js and add this line"import store from "./store";" 5. where it says new "Vue({" add "store" this will register it.

Vuex is super easy and makes life way easier as your project gets bigger.

Gabriel Suttner
  • 157
  • 2
  • 7
0

The .sync modifier provides two way binding pattern to props, you can read about it here https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier.

In the parent component you can use the .sync modifier this way:

<ChildComponent :name.sync="parentNameProperty" />
...
data: () => ({ parentNameProperty: '' }),
...

Then in the child component you receive name as prop and you can emit an event to update the value in the parent component, by using watch or a method:

...
props: {
  name: {
    type: String,
    default: ''
  }
}
...
this.$emit(update:parentNameProperty, newValue)
...

Vuex is a great way to handle state, but is fine to use the above pattern for small applications.

tony19
  • 125,647
  • 18
  • 229
  • 307
Andres Foronda
  • 1,379
  • 1
  • 8
  • 13