Retrieve form data from Vuex state to make edits

I recently completed a tutorial on Vue to enhance my skills, and I'm now facing an issue while trying to make some changes. The tutorial I followed can be found here.

Specifically, I have a "settings" page where users can edit their profile details. When the "settings" or "profile" page loads, I want the form to display their existing data so they can easily make modifications and save them.

Currently, the form loads with placeholders like

:placeholder="userProfile.name"
. Instead, I want the form to populate with the actual values rather than just placeholders.

Although it seems like a simple task, I've been struggling to implement this effectively.

Settings.vue

<template>
  <section id="settings">
    <div class="col1">
      <h3>Settings</h3>
      <p>Update your profile</p>

      <transition name="fade">
        <p v-if="showSuccess" class="success">profile updated</p>
      </transition>

      <form @submit.prevent>
        <label for="name">Name</label>
        <input v-model.trim="name" type="text" id="name" />

        <label for="title">Job Title</label>
        <input v-model.trim="title" type="text" id="title" />

        <button @click="updateProfile()" class="button">Update Profile</button>
      </form>
    </div>
  </section>
</template>

<script>
import { mapState } from "vuex";

export default {
  data() {
    return {
      name: "",
      title: "",
      showSuccess: false,
    };
  },
  computed: {
    ...mapState(["userProfile"]),
  },
  methods: {
    updateProfile() {
      this.$store.dispatch("updateProfile", {
        name: this.name !== "" ? this.name : this.userProfile.name,
        title: this.title !== "" ? this.title : this.userProfile.title,
      });

      this.name = "";
      this.title = "";

      this.showSuccess = true;

      setTimeout(() => {
        this.showSuccess = false;
      }, 2000);
    },
  },
};
</script>

<style lang="scss" scoped>
</style>

After attempting to modify the data section as shown below, the fields work fine when navigating away and returning to the page. However, upon refreshing the page (F5), the fields appear blank until leaving and reentering the page again.

data() {
    return {
      name: this.$store.state.userProfile.name,
      title: this.$store.state.userProfile.title,
      showSuccess: false,
    };
  },

For reference, here is my store configuration:

store/index.js

import Vue from "vue";
import Vuex from "vuex";
import * as fb from "../firebase";
import router from "../router/index";

Vue.use(Vuex);

// Firebase Firestore real-time connection
fb.postsCollection.orderBy("createdOn", "desc").onSnapshot((snapshot) => {
  let postsArray = [];

  snapshot.forEach((doc) => {
    let post = doc.data();
    post.id = doc.id;

    postsArray.push(post);
  });

  store.commit("setPosts", postsArray);
});

const store = new Vuex.Store({
  state: {
    userProfile: {},
    posts: [],
  },
  mutations: {
    setUserProfile(state, val) {
      state.userProfile = val;
    },
    setPosts(state, val) {
      state.posts = val;
    },
  },
  actions: {
    async signup({ dispatch }, form) {
      // User sign up process
      const { user } = await fb.auth.createUserWithEmailAndPassword(
        form.email,
        form.password
      );

      // Create user profile object in userCollections
      await fb.usersCollection.doc(user.uid).set({
        name: form.name,
        title: form.title,
      });

      // Fetch user profile and set in state
      dispatch("fetchUserProfile", user);
    },
    // Other action methods...
  },
  modules: {},
});

export default store;

EDIT

I should note that the profile data is being loaded into the state from Firebase Firestore. It appears to be a timing issue, where the data isn't completely loaded before setting the component's data(). I added some console logs to track the process:

Fetching user profile.. Settings.vue?e12e:29
Setting Data... index.js?4360:75
Performing setUserProfile commit.. index.js?4360:29
Setting user profile in state, last step..

At this stage, my understanding of Vue is limited, making it challenging to determine the best approach to adjust this sequence.

Answer №1

v-model acquires and sets the value of whatever is passed to it. When wanting to edit a state property, modifying the value of an <input> will attempt to change (or mutate) the state property's value, potentially breaking the principle of immutability.

To resolve this issue, a computed property should be passed to v-model, employing both a getter and a setter to specify where the value originates from and how it should be updated.

A default computed property typically contains only a getter function. For example:

computed: {
  name() {
    return this.$store.state.userProfile.name
  }
}

This can alternatively be expressed as:

computed: {
  name: {
    get() {
      return this.$store.state.userProfile.name
    }
  }
}

To ensure proper updating of the state, a setter must be added within the computed property that triggers the necessary mutation:

computed: {
  ...mapState(["userProfile"]),
  name: {
    get() {
      return this.userProfile.name
    },
    set(val) {
      this.$store.commit('setUserProfile', {
        ...this.userProfile,
        name: val
      });
    }
  },
  title: {
    get() {
      return this.userProfile.title
    },
    set(val) {
      this.$store.commit('setUserProfile', {
        ...this.userProfile,
        title: val
      });
    }
  }
}

Documentation on Computed Setters can be found here.


[1] - The usage of Vuex indicates a desire to prevent direct modification of data by components, instead favoring mutations to maintain a single source of truth for the state updates. Allowing v-model to directly alter data would violate the principle of immutability, compromising the integrity of the state as the sole source of truth.

Answer №2

Consider two important factors when accessing the value of a variable in the state crate getters, as recommended by good Vuex practices:

Vuex file:

const store = new Vuex.Store({
  state: {
    userProfile: {},
    posts: [],
  },

  getters:{
    getUserProfile: (state) => state.userProfile
  }

Settigs.vue To achieve your objective, you can load the variables in data() within the mounted method:

export default {
  data() {
    return {
      name: "",
      title: "",
      showSuccess: false,
    };
  },
  computed: {
    ...mapState(["getUserProfile"]),
  },

  mounted(){
   this.name = getUserProfile.name
  }

If you anticipate that the user may refresh the page without losing the loaded data, using Vuex alone will not suffice because the Vuex system also restarts upon page refresh. To maintain the loaded data after a page refresh, consider using local storage in conjunction with Vuex or a similar solution.

Answer №3

Timing plays a crucial role in this scenario:

My recommendation is to connect your data value to a watcher on the state. This way, your component will continuously monitor any updates in the state and adjust your data accordingly.

export default {
  data() {
    return {
      name: "",
      title: "",
      showSuccess: false,
    };
  },
  computed: {
    ...mapState(["userProfile"]),
  },
  watch: {
    userProfile: {
      handler({ name, title }) {
        this.name = name;
        this.title = title;
      },
      deep: true, // Ensures proper object listening
      immediate: true // Triggers the watcher upon component mounting
    }
  },
  methods: {
    updateProfile() {
      this.$store.dispatch("updateProfile", {
        name: this.name !== "" ? this.name : this.userProfile.name,
        title: this.title !== "" ? this.title : this.userProfile.title,
      });
      
            /*  It's advisable not to reset the values here as they will be updated by the watcher
      this.name = "";
      this.title = ""; */

      this.showSuccess = true;

      setTimeout(() => {
        this.showSuccess = false;
      }, 2000);
    },
  },
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Strategically placing elements on an HTML grid

My current project involves creating a grid layout in HTML using CSS. The goal is to use this layout for various elements such as images, text, and links. What I envision is a visually appealing grid where each object fits together seamlessly with no gaps ...

Utilizing JSDoc's "includePattern" for various file formats

Recently, I delved into JSDocs and decided to implement it in my Vue.js project. However, since my project involves multiple file types like .js and .vue, I encountered a syntax error while trying to add them to the "includePattern". Here's the snippe ...

What methods can be used to strategically incorporate gray into both the left and right sides of a page design?

I'm currently working on an asp.net MVC layout page and I'm trying to create a layout where the left side and right side are gray. However, I'm not sure how to achieve this using HTML, CSS, or Bootstrap. I specifically need to know how to ...

Using JQuery to compare duplicate values within the same input name

I am dealing with several hidden input fields like the ones below: <input type="hidden" value="4531" name="product_id"> <input type="hidden" value="4532" name="product_id"> <input type="hidden" value="4533" name="product_id"> My goal is ...

Issue with JQuery Validation: Some checkbox values are not being successfully posted

Currently experiencing issues with validating checkboxes. Utilizing the jQuery plugin validation found here: The scenario is that there are three checkboxes and at least one of them must be checked. The original form code for these checkboxes is as follow ...

Execute a PHP SQL query when a link is clicked

To trigger a SQL query when a link is clicked, I have implemented the following code: mainpage.php: function deleteImage1() { $.post("delete_image.php", { num:1, id:<?php echo $id; ?> }, function(data,status){ alert("Data: " + data + "\n ...

Adding a cell break line within AG-GRID in an Angular application

I'm trying to display two sets of data in a single cell with ag-grid, but I want the data to be on separate lines like this instead: Data with break line I attempted to use "\n", "\r", and "\br" but it didn't work. Here is my code ...

When using a PhoneGap app to access a webservice, are there any potential cross-site issues that could arise?

How can I access an external web service from a PhoneGap app using AJAX without needing to rely on CORS or JSONP to bypass the cross-origin issue? I've come across a question on Stack Overflow suggesting that cross-site HTTP calls are not a problem wi ...

Support for Vue 3.4 same-name shorthand has been added to VS Code

After upgrading my Vue 3 project to version 3.4, I encountered an issue with vs-code highlighting same-name shorthand as an error, even though it was functioning correctly in my code. I am using the volar extension. Is there a way to resolve this so that v ...

What is the process for utilizing jQuery's advanced ticker feature to extract content from a text file?

I am currently implementing this code on my website: <script> var file = "http://newsxpressmedia.com/files/theme/test.txt"; function getData(){ $.get(file,function(txt){ var lines = txt.responseText.split("\n"); for (var i = ...

Optimizing Backend Access with Laravel and Vue JS: How to Choose the Most Effective Approach

Currently, I am utilizing Laravel API passport to handle authentication in my Single Page Application (SPA) built with Vue. At the moment, whenever I need to access the backend server, I have to include a header in order to be allowed through the protected ...

Execute a separate function when clicking on certain buttons without interfering with the buttons' original onclick function (which has already been assigned) - JavaScript

While I am still relatively new to JavaScript and learning, I have developed a webpage with multiple buttons that trigger different functions on click events. However, I would like to additionally call another function when any of these buttons are clicked ...

Experiencing a glitch with the Realtime Database feature on Firebase

// db.js file import * as firebase from "firebase/app" import "firebase/database" const config = { apiKey: "" ... } const db = firebase.initializeApp(config) export default db // App.vue ...

Attempted to create registrations for two views using the identical name RCTScrollView

Having trouble running my React Native app on iOS, I keep getting an error while the Android version works perfectly fine. Does anyone have any insight on this issue? XCode 11.5, RN 0.61.5, Using React Native CLI I've searched multiple sites but hav ...

Identifying the moment an external swf file has finished loading

My JavaScript code is responsible for loading external swf files by adding the "object" and "embed" tags after the page has finished loading. I am looking for a way to detect when the swf file has been fully loaded so that I can handle this event appropr ...

Building a VueJS custom directive that emits an event

Is it possible to trigger an event from a custom directive using $emit? directive.js: vnode.context.$emit("myEvent") // no luck vnode.child.$emit("myEvent") // error vnode.parent.$emit("myEvent") // error component.vue: <div v-directive.modifier= ...

Differentiate input elements with custom styling

I'm experiencing an issue while trying to style a specific form field in Angular 7. The style doesn't seem to be applying properly. Below is my form structure: <ul> <li> <mat-form-field *ngIf="number"> <input ma ...

An error occurred in Node.js while parsing data: Headers cannot be set after they have already been sent to the client

I am currently using a CSV parser to read data from a CSV file. I then pass this data in object format to an EJS template file for printing. However, when I try to stringify the data, I encounter an error. The error message is as follows: Error [ERR_HTTP_ ...

Capture the value of clicked input buttons using v-model when selected

How can I make an arrow appear in the answer field when a button is clicked? https://i.sstatic.net/OC8uA.png <button class="arrow_button">←</button> <button class="arrow_button">↑</button> <button class=& ...

Unexpected outcomes experienced with AJAX in ASP.NET due to javascript integration

I experimented with two methods (server and client side) for initiating a JavaScript AJAX call to post a ticket on a website and then retrieve a link containing the ticket number for tracking or editing purposes. However, both approaches yielded different ...