Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set initial values for data from vuex?

My goal is to create an 'edit account' form such that a user can modify their account data. I want to present the account data in a form that is already filled with the users data i.e username, email, address ...

The user can then modify the data in the form and submit this form that will update their user information.

I am using v-model to bind the form input to an object called accountInfo in my data, that looks like this:

data() {
    return {
        accountInfo: {
                firstName: ''
        }
    }
}

And here is an example of a form input in my template:

<input v-model.trim="accountInfo.firstName" type="text" class="form-control" id="first-name" />

The values for the key's in the object are currently empty strings but I would like the values to come from an object called userProfile that is a state property in vuex.

In my 'edit account' component I am mapping the vuex state by importing:

import { mapState } from "vuex";

then using the following in a computed property

computed: {
    ...mapState(["userProfile"])
}

What I would like to do is instead of having empty strings as the values of accountInfo, assign them values from the userProfile computed property mapped from vuex, like so:

data() {
        return {
            accountInfo: {
                    firstName: this.userProfile.fristName,
            }
        }
    }

This will provide the desired initial data for my form but unfortunately this doesn't work, presumably because data is rendered earlier on in the life cycle than computed properties.

Full code:

EditAccount.vue

<template>
    <div class="container-fluid">
        <form id="sign_up_form" @submit.prevent>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <input v-model.trim="signupForm.firstName" type="text" class="form-control" id="first_name" />
                </div>
            </div>
        </form>
    </div>
</template>

<script>
import { mapState } from "vuex";
import SideBar from "../common/SideBar.vue";

export default {
  name: "EditAccount",
  computed: {
    ...mapState(["userProfile"])
  },
  data() {
    return {
      accountInfo: {
        firstName: this.userProfile.firstName
      }
    };
  }
};
</script>

store.js:

export const store = new Vuex.Store({
    state: {
        userProfile: {firstName: "Oamar", lastName: "Kanji"}
    }
});
like image 527
Oamar Kanji Avatar asked Jun 27 '18 13:06

Oamar Kanji


People also ask

How do I update my Vuex data?

To create new data, you can use the insert , create , and new methods. They all insert new records to the Vuex Store but behave in a slightly different manner. The insert method will simply insert new records. You should pass an object containing records in data key to the method.

What is mapState Vuex?

Mapping State Vuex provides a helper function called mapState to solve this problem. It is used for mapping state properties in the store to computed properties in our components. import { mapState } from 'vuex' 2. export default{

Does Vuex store reset on refresh?

Generally, when the login is successful, the user information and menu information need to be placed in vuex as global shared data. However, when the page is refreshed, the data in vuex will be reinitialized, resulting in data loss.


1 Answers

You were right, computeds are evaluated after the initial data function is called.

Quick fix

In the comments, @Jacob Goh mentioned the following:

$store should be ready before data function is called. Therefore, firstName: this.$store.state.userProfile.firstName should just work.

export default {
  name: 'EditAccount',
  data() {
    return {
      accountInfo: {
        firstName: this.$store.state.userProfile.firstName
      }
    }
  }
};

Really need computeds?

See @bottomsnap's answer, where setting the initial value can be done in the mounted lifecycle hook.

With your code, it would look like this:

import { mapState } from 'vuex';

export default {
  name: 'EditAccount',
  computed: {
    ...mapState(['userProfile'])
  },
  data() {
    return {
      accountInfo: {
        firstName: ''
      }
    }
  }
  mounted() {
    this.accountInfo.firstName = this.userProfile.firstName;
  }
};

Though it may render once without the value, and re-render after being mounted.

Container versus presentation

I explain Vue's communication channels in another answer, but here's a simple example of what you could do.

Treat the Form component as presentation logic, so it doesn't need to know about the store, instead receiving the profile data as a prop.

export default {
    props: {
        profile: {
            type: Object,
        },
    },
    data() {
        return {
            accountInfo: {
                firstName: this.profile.firstName
            }
        };
    }
}

Then, let the parent handle the business logic, so fetching the information from the store, triggering the actions, etc.

<template>
    <EditAccount :profile="userProfile" :submit="saveUserProfile"/>
</template>
<script>
import { mapState, mapActions } from "vuex";

export default {
    components: { EditAccount },
    computed: mapState(['userProfile']),
    methods: mapActions(['saveUserProfile'])
}
</script>

While Jacob is not wrong saying that the store is ready, and that this.$store.state.userProfile.firstName will work, I feel this is more a patch around a design problem that can easily be solved with the solution above.

like image 102
Emile Bergeron Avatar answered Sep 21 '22 22:09

Emile Bergeron