J'ai une section de profil d'utilisateur et j'essaye de permettre à l'utilisateur de modifier ses informations. J'utilise vuex pour stocker les données du profil utilisateur et les extraire dans le formulaire. Le formulaire d'édition est situé dans un composant enfant du composant userProfile - qui charge la sauvegarde des données la valide dans VUEX.

Je peux donc remplir le formulaire avec les données de VUEX, mais dès que je change les valeurs du formulaire, cela change également la valeur de mon composant parent.

Je ne valide pas les modifications dans VUEX tant que le formulaire n'est pas enregistré, cela signifie donc que les données sont liées dans les deux sens à VUEX. J'avais l'impression que ce n'était pas possible. Dans ce cas, ce n'est pas souhaité car si l'utilisateur modifie certaines données, puis s'en va sans cliquer réellement sur "enregistrer", les données sont VUEX est toujours modifiée.

Notez qu'il s'agit d'un exemple simplifié. J'utilise en fait la vue du routeur pour charger le composant enfant, ou je passerais les données via des accessoires. J'ai testé le chargement du composant de profil d'édition directement comme il est ci-dessous, et j'ai le même problème.

Veuillez consulter le code ci-dessous, je ne trouve pas pourquoi les données sont renvoyées au magasin. Toute aide est grandement appréciée.

Dans le parent, je définis récupérer les données utilisateur comme ceci:

<template>
    <div class="content">
        <h1>{{getUserDetails.firstname}} {{getUserDetails.lastname}} </h1>
        <edit-profile></edit-profile>
    </div>
</template>
<script>
    import { mapGetters } from 'vuex';
    import EditProfile from './Edit.vue';

    export default {
        data() {
            return {
                // data
            }
        },
        created () {
            this.fetchData();
        },
        components: {
            EditProfile:EditProfile
        },
        computed: mapGetters([
            'getUserDetails'
        ]),
        methods: {
            fetchData: function () {
                var _this = this;
                // ajax call - then
                _this.$store.commit('setData', {
                    name: 'userDetails',
                    data: response.data.data.userDetails
                });
            }
        }
    }
</script>

Cela charge les résultats et les stocke dans le magasin, et fonctionne très bien.

Mon magasin a ceci:

const store = new Vuex.Store({
    state: {
        userDetails: {}
    },
    mutations: {
        setData(state, payload){
            state[payload.name] = payload.data;
        }
    },
    getters: {
        getUserDetails: state => {
            return state.userDetails;
        }
    }
}

Tout ici fonctionne.

Dans mon composant enfant avec le formulaire d'édition, je remplis le formulaire comme ceci:

<template>
    <form>
        <label>Name</label>
        <input name="firstname" v-model="profile.firstname">
        <input name="lastname" v-model="profile.lastname">
        <button v-on:click="save">submit</button>
     </form>
</template>

<script>
import {mapGetters } from 'vuex';

export default {
    data() {
        return {
            profile:{}
        }
    },
    watch: {
        getUserDetails (newData){
            this.profile = newData;
        }
    },
    created (){
        this.profile = this.$store.getters.getUserDetails;
    },
    computed: mapGetters([
        'getUserDetails'
    ]),
    methods:{
        save (){
            var _this = this;
            // ajax call to POST this.profile then
            _this.$store.commit('setData', {
                name: 'userDetails',
                data: this.profile
            });
        }
    }
}
</script>
10
Jeff Ryan 26 janv. 2017 à 00:37

4 réponses

Meilleure réponse

Si vous recherchez une solution non contraignante avec vuex, vous pouvez cloner l'objet et utiliser la version locale pour le v-model que lors de l'envoi, le valider.

Dans votre fonction de cycle de vie créée, procédez comme suit:

created (){
    this.profile = Object.assign({}, this.$store.getters.getUserDetails);
},
11
AfikDeri 25 janv. 2017 à 22:23

La solution @AfikDeri est excellente, mais elle ne crée qu'une copie superficielle (par exemple, cela ne fonctionnera pas si vous avez des objets imbriqués, ce qui est courant d'avoir), pour résoudre ce problème, vous pouvez sérialiser puis analyser votre objet d'état vuex getUserDetails, comme suit:

created (){
    this.profile = JSON.parse(JSON.stringify(this.$store.getters.getUserDetails));
}
0
adnanmuttaleb 22 mars 2020 à 08:45

Pourquoi je pense que cela ne fonctionne pas comme prévu pour vous: vous recevez un objet, le liant à une propriété locale. Ensuite, lorsque vous modifiez cette propriété locale, elle est liée par un pointeur d'objet (/ adresse mémoire) à l'objet du magasin. Créer un nouvel objet et définir les propriétés de ce nouvel objet en fonction des propriétés de l'objet de profil utilisateur de l'état devrait faire l'affaire, car le nouvel objet aurait sa propre adresse en mémoire, pointerait vers un autre endroit ...

Illustration:

created (){
    // create a new object with {...}
    this.profile = {
        // assign values to properties of same name
        firstName: this.$store.getters.getUserDetails.firstName,
        lastName: this.$store.getters.getUserDetails.lastName,
    };
},

Cependant si ces propriétés (firstName, lastName) sont des objets ou des tableaux (tout ce qui est accédé par un pointeur vers une adresse mémoire), cela ne fonctionnera pas non plus.


Donc ... ce que je finirais probablement par faire moi-même dans cette situation est quelque chose comme ceci:

data() {
    return {
        firstName: '',
        lastName: ''
    }
},

Cela définit les propriétés locales. Lors du chargement des données, vous remplissez les valeurs locales avec les données de profil que vous avez dans le magasin Vuex.

created() {
    let profile = this.$store.getters.getUserDetails;
    if (profile.firstName && profile.lastName) {
        this.firstName = profile.firstName;
        this.lastName = profile.lastName;
    }
},

Ensuite, lors de l'enregistrement, vous utilisez vos variables locales pour mettre à jour les valeurs du magasin.

methods: {
    save() {
        let profile = {
            firstName: this.firstName,
            lastName: this.lastName
        };

        // ajax call to POST this.profile then
        this.$store.commit('setData', {
            name: 'userDetails',
            data: profile
        });
    }
}

J'écris ceci du haut de ma tête, donc il pourrait y avoir un bug ou une faute de frappe ici ... Mais j'espère au moins que ma logique est correcte ;-P et claire pour vous.

Pro : tant que vous n'êtes pas prêt à enregistrer les informations modifiées, vous ne les reflétez nulle part ailleurs.

Contre : si vous avez besoin de refléter des modifications temporaires (peut-être dans une zone d'aperçu du profil utilisateur), cela peut ou non fonctionner selon la structure de votre application. Vous pourriez vouloir lier ou enregistrer sur @input à un objet state.temporaryUserProfile dans ce cas?

Je suis encore nouveau sur Vue.js, j'ai commencé à l'utiliser il y a 2 semaines. J'espère que c'est clair et correct :)

1
LittleTiger 16 juil. 2017 à 04:39

Le problème est causé par l'utilisation de v-model avec mapGetters - cela crée la liaison bidirectionnelle que vous avez décrite. La solution simple consiste à utiliser la valeur à la place:

:value="profile.firstName"

De cette façon, le formulaire ne modifie que la copie locale du champ et ne renvoie pas les modifications au magasin Vuex.

0
James Beswick 6 juin 2018 à 19:46