简体   繁体   English

VueJS DOM未在组件中更新

[英]VueJS DOM not updating in component

I have gone through the other "DOM not updating" posts, but have not been able to figure out a solution. 我已经看过其他“ DOM不更新”文章,但是还没有找到解决方案。 I am trying to create a task app for starters and the app can already load from Firestore, add a new one and delete one. 我正在尝试为初学者创建一个任务应用,并且该应用已经可以从Firestore加载,添加一个新应用并删除其中一个。 I have two problems, but I would like to start with the first one. 我有两个问题,但我想从第一个开始。 When I add, delete data, the DOM is not updating dynamically, although the respective array in the Vuex store is updated correctly. 当我添加,删除数据时,尽管Vuex存储中的各个数组已正确更新,但DOM并未动态更新。 When I reload the page, it is also updating. 当我重新加载页面时,它也在更新。

I have added the code of the component and the store, I hope I am looking the in the right place, but I have the feeling I am not. 我已经添加了组件和商店的代码,希望在正确的位置查找,但是我有一种感觉。

Thanks in advance 提前致谢

Store code: 商店代码:

import Vue from 'vue'
import Vuex from 'vuex'
// import firebase from 'firebase'
import router from '@/router'
import db from '@/db'
import firebase from '@/firebase'

Vue.use(Vuex)

export const store = new Vuex.Store({
  state: {
    appTitle: 'My Awesome App',
    addedTask: false,
    loadedTasks: false,
    deletedTasks: false,
    user: null,
    error: null,
    loading: false,
    tasks: [],
    task: {
      title: '',
      description: '',
      tags: [],
      dueTime: '',
      dueDate: '',
      reminderFlag: Boolean,
      quadrant: '',
      userId: '',
      timestamp: ''
    }
  },
  // Mutations (synchronous): change data in the store
  mutations: {
    setUser (state, payload) {
      state.user = payload
      state.task.userId = firebase.auth().currentUser.uid
    },
    setError (state, payload) {
      state.error = payload
    },
    setLoading (state, payload) {
      state.loading = payload
    },
    addedTask (state, payload) {
      state.addedTask = payload
    },
    loadedTasks (state, payload) {
      state.loadedTasks = true
    },
    deletedTask (state, payload) {
      state.deletedTask = payload
    }
  },
  // Actions (asynchronous/synchronous): change data in the store
  actions: {
    autoSignIn ({ commit }, payload) {
      commit('setUser', { email: payload.email })
    },
    userSignUp ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          router.push('/home')
          commit('setError', null)
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignIn ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          commit('setError', null)
          router.push('/home')
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignOut ({ commit }) {
      firebase.auth().signOut()
      commit('setUser', null)
      router.push('/')
    },
    addNewTask ({ commit }, payload) {
      commit('addedTask', false)
      db.collection('tasks').add({
        title: payload.title,
        userId: firebase.auth().currentUser.uid,
        createdOn: firebase.firestore.FieldValue.serverTimestamp(),
        description: payload.description,
        dueDateAndTime: new Date(payload.dueDate & ' ' & payload.dueTime),
        reminderFlag: payload.reminderFlag,
        quadrant: payload.quadrant,
        tags: payload.tags
      })
        .then(() => {
          commit('addedTask', true)
          this.state.tasks.push(payload)
          commit('loadedTasks', true)
        })
        .catch(error => {
          commit('setError', error.message)
        })
    },
    getTasks ({ commit }) {
      commit('loadedTasks', false)
      db.collection('tasks').orderBy('createdOn', 'desc').get().then(querySnapshot => {
        querySnapshot.forEach(doc => {
          const data = {
            'taskId': doc.id,
            'title': doc.data().title,
            'quadrant': doc.data().quadrant
          }
          this.state.tasks.push(data)
        })
        commit('loadedTasks', true)
      })
    },
    deleteTask ({ commit }, payload) {
      db.collection('tasks').doc(payload).delete().then(() => {
        this.state.tasks.pop(payload)
        commit('deletedTask', true)
        commit('loadedTasks', true)
      }).catch(function (error) {
        console.error('Error removing document: ', error)
        commit('deletedTask', false)
      })
    }
  },
  // Getters: receive data from the store
  getters: {
    isAuthenticated (state) {
      return state.user !== null && state.user !== undefined
    }
  }
})

This is the component (Vue app -> Home -> Grid -> Tasklist (this): 这是组件(Vue应用程序->主页->网格->任务列表(此):

<template>

  <v-card>

    <v-card-title primary class="title">{{title}}</v-card-title>

    <v-flex v-if="tasks.length > 0">
      <v-slide-y-transition class="py-0" group tag="v-list">
        <template v-for="(task, i) in tasks" v-if="task.quadrant === Id">
          <v-divider v-if="i !== 0" :key="`${i}-divider`"></v-divider>

          <v-list-tile :key="`${i}-${task.text}`">
            <v-list-tile-action>
              <v-checkbox v-model="task.done" color="info darken-3">
                <div slot="label" :class="task.done && 'grey--text' || 'text--primary'" class="text-xs-right" v-text="task.title +' ('+ task.taskId +')'"></div>
              </v-checkbox>
            </v-list-tile-action>
            <v-spacer></v-spacer>
            <v-scroll-x-transition>
              <v-btn flat icon color="primary" v-show="!deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
                <v-icon>delete</v-icon>
              </v-btn>
            </v-scroll-x-transition>

            <v-scroll-x-transition>
              <v-btn flat icon color="green" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
                <v-icon>cancel</v-icon>
              </v-btn>
            </v-scroll-x-transition>

            <v-scroll-x-transition>
              <v-btn flat icon color="red" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation; deleteTask(task.taskId)">
                <v-icon>check_circle</v-icon>
              </v-btn>
            </v-scroll-x-transition>

          </v-list-tile>
        </template>
      </v-slide-y-transition>
    </v-flex>
  </v-card>
</template>

<script>

export default {
  data () {
    return {
      deleteConfirmation: false
    }
  },
  props: ['Id', 'title'],
  computed: {
    tasks () {
      return this.$store.state.tasks
    }
  },
  methods: {
    deleteTask (taskId) {
      console.log(taskId)
      this.$store.dispatch('deleteTask', taskId)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
.container {
  max-width: 1200px;
}
</style>

You have no mutation to actually change your tasks array, as eg this.state.tasks.pop(payload) happens in the deleteTask action, not the mutation. 您无需更改即可实际更改tasks数组,例如this.state.tasks.pop(payload)发生在deleteTask操作中,而不是更改。

The only way to actually change state in a Vuex store is by committing a mutation. 实际更改Vuex存储中状态的唯一方法是提交一个突变。 https://vuex.vuejs.org/guide/mutations.html https://vuex.vuejs.org/guide/mutations.html

That's also the reason why you see right result after reloading: The Firestore implementation is fine and updates its values. 这也是您在重新加载后看到正确结果的原因:Firestore实现很好并且可以更新其值。 It will show the updated and newly fetched tasks array after reloading, but the Vuex state never gets changed before reloading, so Vue's displaying of the tasks array stays the same. 重新加载 ,它将显示更新后的和新获取的tasks数组,但是重新加载 Vuex状态永远不会改变,因此Vue对tasks数组的显示保持不变。

The solution to this is pretty straightforward: 解决方案非常简单:

Create a new mutation: 创建一个新的突变:

REMOVE_FROM_TASKS (state, payload) {
    // algorithm to remove, e. g. by id
    state.tasks = state.tasks.filter(e => {
        return e.id !== payload;
    });
},

Use this mutation in your action: 在操作中使用此突变:

deleteTask ({ commit }, payload) {
  db.collection('tasks').doc(payload).delete().then(() => {
    commit('REMOVE_FROM_TASKS', payload)
    commit('deletedTask', true)
    commit('loadedTasks', true)
  }).catch(function (error) {
    console.error('Error removing document: ', error)
    commit('deletedTask', false)
  })
}

the DOM now updates on deletes properly. DOM现在会在删除后正确更新。 I changes the structure of the components a little bit. 我稍微更改了组件的结构。 It is now: Vue app -> Home -> Grid -> Tasklist -> TaskListItem. 现在是:Vue app->主页->网格-> Tasklist-> TaskListItem。 I am still facing the following issue: Adding an item updates the DOM, but when I add multiple entries, eg with different titles, they are written to Firestore correctly, but in the DOM it shows them all as identical until I do a complete reload. 我仍然面临以下问题:添加项目会更新DOM,但是当我添加多个条目(例如具有不同标题的条目)时,它们会正确写入Firestore,但是在DOM中,它们会显示为相同,直到我完全重新加载。

Please find the code below: 请在下面找到代码:

Store: 商店:

import Vue from 'vue'
import Vuex from 'vuex'
// import firebase from 'firebase'
import router from '@/router'
import db from '@/db'
import firebase from '@/firebase'

Vue.use(Vuex)

export const store = new Vuex.Store({
  state: {
    appTitle: 'My Awesome App',
    taskFormVisible: false,
    addedTask: false,
    loadedTasks: false,
    deletedTasks: false,
    user: null,
    error: null,
    loading: false,
    tasks: [],
    task: {
      title: '',
      description: '',
      tags: [],
      dueTime: '',
      dueDate: '',
      reminderFlag: Boolean,
      quadrant: '',
      userId: '',
      timestamp: ''
    }
  },
  // Mutations (synchronous): change data in the store
  mutations: {
    setUser (state, payload) {
      state.user = payload
      state.task.userId = firebase.auth().currentUser.uid
    },
    setError (state, payload) {
      state.error = payload
    },
    setLoading (state, payload) {
      state.loading = payload
    },
    addedTask (state, payload) {
      state.addedTask = payload
    },
    loadedTasks (state, payload) {
      state.loadedTasks = true
    },
    deletedTask (state, payload) {
      state.deletedTask = payload
    },
    DELETE_TASK (state, payload) {
      state.tasks = state.tasks.filter(task => {
        return task.taskId !== payload
      })
    },
    ADD_NEW_TASK (state, payload) {
      // algorithm to remove, e. g. by id
      // console.log(payload)
      state.tasks.push(payload)
      /* state.tasks = state.tasks.filter(e => {
        return e.id !== payload
      }) */
    },
    GET_TASKS (state, payload) {
      state.tasks = payload
    },
    MAKE_TASKFORM_VISIBLE (state, payload) {
      state.taskFormVisible = payload
    }

  },
  // Actions (asynchronous/synchronous): change data in the store
  actions: {
    autoSignIn ({ commit }, payload) {
      commit('setUser', { email: payload.email })
    },
    userSignUp ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          router.push('/home')
          commit('setError', null)
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignIn ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          commit('setError', null)
          router.push('/home')
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignOut ({ commit }) {
      firebase.auth().signOut()
      commit('setUser', null)
      router.push('/')
    },
    makeTaskformVisible ({ commit }) {
      if (this.state.taskFormVisible === false) {
        commit('MAKE_TASKFORM_VISIBLE', true)
      } else {
        commit('MAKE_TASKFORM_VISIBLE', false)
      }
    },
    addNewTask ({ commit }, payload) {
      db.collection('tasks').add({
        title: payload.title,
        userId: firebase.auth().currentUser.uid,
        createdOn: firebase.firestore.FieldValue.serverTimestamp(),
        description: payload.description,
        dueDateAndTime: payload.dueTimestamp,
        reminderFlag: payload.reminderFlag,
        quadrant: payload.quadrant,
        tags: payload.tags,
        updatedOn: firebase.firestore.FieldValue.serverTimestamp()
      })
        .then(() => {
          commit('ADD_NEW_TASK', payload)
        })
        .catch(error => {
          commit('setError', error.message)
        })
    },
    getTasks ({ commit }) {
      // commit('loadedTasks', false)
      db.collection('tasks').orderBy('createdOn', 'desc').get().then(querySnapshot => {
        querySnapshot.forEach(doc => {
          const data = {
            'taskId': doc.id,
            'title': doc.data().title,
            'quadrant': doc.data().quadrant
          }
          this.state.tasks.push(data)
        })
        // commit('loadedTasks', true)
        commit('GET_TASKS', this.state.tasks)
      })
    },
    deleteTask ({ commit }, payload) {
      db.collection('tasks').doc(payload).delete().then(() => {
        commit('DELETE_TASK', payload)
      }).catch(function (error) {
        // console.error('Error removing document: ', error)
        commit('setError', error.message)
      })
    }
  },
  // Getters: receive data from the store
  getters: {
    isAuthenticated (state) {
      return state.user !== null && state.user !== undefined
    }
  }
})

TaskList: 任务列表:

<template>

  <ul>
    <li>
      <h4>{{title}}</h4>
    </li>
    <li v-for="task in tasks" :key="task.taskId" v-if="task.quadrant === Id" class="collection-item">
      <task-list-item v-bind:task="task"></task-list-item>
    </li>
  </ul>
</template>

<script>
import TaskListItem from './TaskListItem'

export default {
  components: {
    'task-list-item': TaskListItem
  },
  data () {
    return {

    }
  },
  props: ['Id', 'title'],
  computed: {
    tasks () {
      return this.$store.state.tasks
    }
  },
  methods: {

  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>

</style>

TaskListItem: TaskListItem:

<template>
  <div>{{task.title}} ({{task.taskId}})<v-spacer></v-spacer>
    <v-scroll-x-transition>
      <v-btn flat icon color="primary" v-show="!deleteConfirmation">
        <v-icon>edit</v-icon>
      </v-btn>
    </v-scroll-x-transition>
    <v-scroll-x-transition>
      <v-btn flat icon color="primary" v-show="!deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
        <v-icon>delete</v-icon>
      </v-btn>
    </v-scroll-x-transition>

    <v-scroll-x-transition>
      <v-btn flat icon color="green" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
        <v-icon>cancel</v-icon>
      </v-btn>
    </v-scroll-x-transition>

    <v-scroll-x-transition>
      <v-btn flat icon color="red" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation; deleteTask(task.taskId)">
        <v-icon>check_circle</v-icon>
      </v-btn>
    </v-scroll-x-transition>
    </li>
  </div>

</template>

<script>

export default {
  data () {
    return {
      deleteConfirmation: false
    }
  },
  props: ['task'],
  computed: {

  },
  methods: {
    deleteTask (taskId) {
      this.$store.dispatch('deleteTask', taskId)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>

</style>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM