Vuejs 2,VUEX,编辑数据时的数据绑定

Vuejs 2, VUEX, data-binding when editing data

我有一个用户个人资料部分,我正在尝试允许用户编辑他们的信息。我正在使用 vuex 来存储用户配置文件数据并将其拉入表单。编辑表单位于 userProfile 组件的子组件中 - 加载数据保存将其提交给 VUEX。

所以我可以用来自 VUEX 的数据填充表单,但是一旦我更改表单中的任何值,它也会更改我父组件中的值。

在保存表单之前我不会提交对 VUEX 的更改,因此这意味着数据以两种方式绑定到 VUEX。我的印象是这是不可能的。在这种情况下,这是不希望的,因为如果用户更改了一些数据,然后在没有实际单击 "save" 的情况下导航离开,VUEX 的数据仍然在更改。

请注意,这是一个简化的示例。我实际上使用路由器视图来加载子组件,或者我会通过道具传递数据。我已经测试过直接加载 edit-profile 组件,如下所示,我遇到了同样的问题。

请查看下面的代码,我找不到为什么将数据发送回商店。任何帮助是极大的赞赏。

在父级中,我设置检索用户数据如下:

<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>

这会加载结果并将它们存储在商店中,效果很好。

我的商店有这个:

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

这里一切正常。

在我的带有编辑表单的子组件中,我正在填充这样的表单:

<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>

如果您正在寻找使用 vuex 的非绑定解决方案,您可以克隆该对象并使用本地版本 v-model 而不是提交提交它。

在您创建的生命周期函数中执行此操作:

created (){
    this.profile = Object.assign({}, this.$store.getters.getUserDetails);
},

为什么我 认为 它没有按您预期的那样工作:您正在接收一个对象,将其绑定到本地 属性。然后,当您更改该本地 属性 时,它会通过对象指针(/内存地址)绑定到商店的对象。 创建一个新对象并根据状态的用户配置文件对象的属性在该新对象上设置属性应该可以解决问题,因为新对象将在内存中拥有自己的地址,将指向另一个地方...

插图:

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,
    };
},

但是如果这些属性(firstName、lastName)是对象或数组(任何通过指向内存地址的指针访问的东西)那么这也不起作用。


所以...在这种情况下我最有可能最终做的事情是这样的:

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

这定义了本地属性。加载数据时,您将使用 Vuex 存储中的配置文件数据填充本地值。

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

然后,在保存时,您使用局部变量来更新商店的值。

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
        });
    }
}

我是从头到尾写的,所以这里可能有错误或打字错误......但我希望至少我的逻辑是正确的;-P 并且你明白了。

Pro:在您准备好保存编辑的信息之前,您不会在其他任何地方反映它。

Con:如果您需要反映临时更改(可能在用户配置文件预览区域),这可能有效也可能无效,具体取决于您应用的结构。在这种情况下,您可能希望将 @input 绑定或保存到 state.temporaryUserProfile 对象?

我还是 Vue.js 的新手,2 周前开始使用它。希望这是清楚和正确的:)

问题是由将 v-model 与 mapGetters 一起使用引起的 - 这会创建您所描述的双向绑定。简单的解决方案是改用值:

:value="profile.firstName"

这样表单只会更改字段的本地副本,不会将更改推送回 Vuex 存储。

@Afik​​Deri 解决方案很棒,但它只会创建一个浅拷贝(例如,如果你有嵌套对象,它就不会工作,这是很常见的),要解决这个问题,你可以 serialize然后parse你的vuex状态对象getUserDetails,如下:

created (){
    this.profile = JSON.parse(JSON.stringify(this.$store.getters.getUserDetails));
}