web-dev-qa-db-fra.com

Quelle est la manière la plus saine de manipuler les formulaires Vuejs + Vuex?

J'ai un gros formulaire à soumettre en une seule page.

<container>
  <formA>
  <formB>
  <formC>
  <submitButton>
<container>

cela ressemble apparemment à ceci. et j'ai un magasin qui enregistre toutes les données de formulaire. puis lorsque l'utilisateur clique sur le bouton d'envoi, je rassemble toutes les données de formulaire à l'aide de vuex store.

Le problème est que je dois mettre à jour les données de formulaire en magasin à chaque fois.

donc je serai comme ça composante en vue

 watch: {
   userInput (val) {
     this.updateState(val)
 }

mise à jour de l'état lorsque l'entrée change en regardant les données de formulaire (liées avec v-model).

ou comme celui-ci qui est documenté dans la vuex doc.

  userInput: {
    get () {
      return this.$store.state.userInput
    },
    set (val) {
      this.updateState(val)
    }
  }

bien .. Je ne pense pas que ce soit une bonne idée. Existe-t-il un meilleur moyen de gérer le traitement avec Vuex?

8
mjkim

J'ai créé un petit outil qui facilite beaucoup la manipulation des formulaires avec Vuex: vuex-map-fields

Exemple

Le magasin

import Vue from 'vue';
import Vuex from 'vuex';

// Import the `getField` getter and the `updateField`
// mutation function from the `vuex-map-fields` module.
import { getField, updateField } from 'vuex-map-fields';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    fieldA: '',
    fieldB: '',
  },
  getters: {
    // Add the `getField` getter to the
    // `getters` of your Vuex store instance.
    getField,
  },
  mutations: {
    // Add the `updateField` mutation to the
    // `mutations` of your Vuex store instance.
    updateField,
  },
});

Composant

<template>
  <div id="app">
    <input v-model="fieldA">
    <input v-model="fieldB">
  </div>
</template>

<script>
import { mapFields } from 'vuex-map-fields';

export default {
  computed: {
    // The `mapFields` function takes an array of
    // field names and generates corresponding
    // computed properties with getter and setter
    // functions for accessing the Vuex store.
    ...mapFields([
      'fieldA',
      'fieldB',
    ]),
  },
};
</script>

Vous pouvez en savoir plus sur les champs vuex-map-fields sur mon blog: Comment gérer les formulaires à plusieurs lignes avec Vue, Vuex et vuex-map-fields

4
moriartie

J'utiliserais des observateurs approfondis pour cela et disposerais de tous les champs d'un objet. Vous pouvez utiliser plusieurs méthodes pour enregistrer les données, effectuer une itération sur Object.keys pour stocker chaque champ avec son nom de variable dans l'objet de formulaire, ou stocker l'ensemble du formulaire, peu importe. vous pourriez avoir besoin.

Vous pouvez également utiliser v-model.lazy="form.myfield" pour indiquer que vous souhaitez que la liaison ne soit mise à jour que lorsque l'utilisateur aura quitté le champ.

Composant de formulaire

<template>
    <div>
        <!-- You can optionally use v-model.lazy="form.field1" to only update once user has exited the field or pressed enter -->
        <input v-model="form.field1" />
        <input v-model.lazy="form.field2" />
    </div>
</template>

<script>
    export default {
        props: ['value'],

        data: function () {
            return {
                internalForm: {
                    field1: null,
                    field2: null
                }
            }
        },

        watch: {
            internalForm: {
                handler: function (newValue) {
                // Emit new form object to parent component so we can use v-model there
                this.$emit('input', this.form)
                // Or save form data
                this.handleFormSave(this.form)
                },
                // Tell vue to do a deep watch of entire form object to watch child items in the object
                deep: true
            }
        }
    }
</script>

Composant parent

<template>
    <form-component v-model="forms.form1" />
    <submit-button @click="saveAllFormData" />
</template>

<script>
    export default {
        data: function () {
            return {
                forms: {
                    form1: null // This will be updated when 'input' is emitted 
                }
            }
        },

        watch: {
            forms: {
                handler: function (newValue) {
                    if (allFormsValid && readyToSave)
                        saveAllFormData(newValue);
                },
                deep: true
            }
        }
    }
</script>
3
Faylite

J'avais mal à la tête à propos de ce problème.

Vuex doc décrit la nécessité de mettre à jour le magasin pour chaque champ. C’est un butin de taper quoi?

Nous proposons une solution qui fonctionne. Elle est basée sur le clonage d’un objet de magasin vers un objet local.

  //We are passing (vuexstore) 'item' object from parent component:
  //<common-item v-bind:item="item" ....
  props: ['item'],

  // create localItem - this is reactive object for vuex form
  data: () => {
    return {
      localItem: null
    }
  },

  // make clone on created event
  created: function() {
    this.localItem =  this._clone(this.item)
  },

  // watch vuexstore 'item' for changes
  watch: {
    item: function(val) {
      this.localItem = this._clone(this.item)
    }
  },

  // map mutations and update store on event
  methods: {
     ...mapMutations([
      'editItem'
    ]),
    updateItemHandler: function() {
      this.editItem({ item: this._clone(this.localItem) })
    },
    _clone: function(o){
      return JSON.parse(JSON.stringify(o))
    }
  },

Utilisation de la forme interne

 <input v-model="localItem.text" @keyup="updateItemHandler" type="text" class="form-control"></input>

Je pense que ce n'est que le manque de vuex. Il devrait y avoir une solution beaucoup plus courte et intégrée.

1
Srdjan Arsic