Vue.js:子组件改变状态,父组件显示属性未定义

Posted

技术标签:

【中文标题】Vue.js:子组件改变状态,父组件显示属性未定义【英文标题】:Vue.js: Child Component mutates state, parent displays property as undefined 【发布时间】:2021-06-27 15:13:09 【问题描述】:

我有一个列出所有任务的父组件:

<template>
  <div class="tasks-wrapper">
    <div class="tasks-header">
      <h4> $t('client.taskListingTitle') </h4>
      <b-button variant="custom" @click="showAddTaskModal"> $t('client.addTask') </b-button>
    </div>
      <b-table
        striped
        hover
        :items="tasks"
        :fields="fields"
        show-empty
        :empty-text="$t('common.noResultsFound')">
      </b-table>
    <AddTaskModal />
  </div>
</template>

<script>
import  mapActions, mapGetters  from 'vuex'
import AddTaskModal from '@/components/modals/AddTaskModal'
import moment from 'moment'

export default 
  name: 'TaskListing',
  components: 
    AddTaskModal
  ,
  data () 
    return 
      tasks: [],
      fields: [
         key: 'createdOn', label: this.$t('tasks.tableFields.date'), formatter: 'formatDate' ,
         key: 'domain', label: this.$t('tasks.tableFields.task') ,
         key: 'comment', label: this.$t('tasks.tableFields.comment') ,
         key: 'status', label: this.$t('tasks.tableFields.status') 
      ]
    
  ,
  computed: 
    ...mapGetters('users', ['user'])
  ,
  methods: 
    ...mapActions('tasks', ['fetchTasks']),
    ...mapActions('users', ['fetchUserById']),
    formatDate: function (date) 
      return moment.utc(date).local().format('DD.MM.YYYY HH:mm')
    ,
    showAddTaskModal () 
      this.$bvModal.show('addTaskModal')
    
  ,
  async mounted () 
    const currUserId = this.$router.history.current.params.id
    if (this.user || this.user.userId !== currUserId) 
      await this.fetchUserById(currUserId)
    
    if (this.user.clientNumber !== null) 
      const filters =  clientReferenceNumber:  value: this.user.clientNumber  
      this.tasks = await this.fetchTasks( filters )
    
  

</script>

在这个组件内部有一个添加任务模式的子组件。

<template>
  <b-modal
    id="addTaskModal"
    :title="$t('modals.addTask.title')"
    hide-footer
    @show="resetModal"
    @hidden="resetModal"
  >
    <form ref="form" @submit.stop.prevent="handleSubmit">
      <b-form-group
        :invalid-feedback="$t('modals.requiredFields')">
        <b-form-select
          id="task-type-select"
          :options="taskTypesOptions"
          :state="taskTypeState"
          v-model="taskType"
          required
        ></b-form-select>
        <b-form-textarea
          id="add-task-input"
          :placeholder="$t('modals.enterComment')"
          rows="3"
          max-rows="6"
          v-model="comment"
          :state="commentState"
          required />
      </b-form-group>
      <b-button-group class="float-right">
        <b-button variant="danger" @click="$bvModal.hide('addTaskModal')"> $t('common.cancel') </b-button>
        <b-button @click="addTask"> $t('modals.addTask.sendMail') </b-button>
      </b-button-group>
    </form>
  </b-modal>
</template>

<script>
import  mapActions, mapGetters  from 'vuex'

export default 
  name: 'AddTaskModal',
  data () 
    return 
      comment: '',
      commentState: null,
      taskTypesOptions: [
         value: null, text: this.$t('modals.addTask.taskType') ,
         value: 'OnBoarding', text: 'Onboarding' ,
         value: 'Accounts', text: 'Accounts' ,
         value: 'TopUp', text: 'Topup' ,
         value: 'Overdraft', text: 'Overdraft' ,
         value: 'Aml', text: 'Aml' ,
         value: 'Transfers', text: 'Transfers' ,
         value: 'Consultation', text: 'Consultation' ,
         value: 'TechnicalSupport', text: 'TechnicalSupport' ,
         value: 'UnblockPin', text: 'UnblockPin' ,
         value: 'Other', text: 'Other' 
      ],
      taskType: null,
      taskTypeState: null
    
  ,
  computed: 
    ...mapGetters('users', ['user']),
    ...mapGetters('tasks', ['tasks'])
  ,
  methods: 
    ...mapActions('tasks', ['addNewTask', 'fetchTasks']),
    ...mapActions('users', ['fetchUserById']),
    async addTask (bvModalEvt) 
      bvModalEvt.preventDefault()
      if (!this.checkFormValidity())  return 
      const currUserId = this.$router.history.current.params.id
      if (this.user || this.user.userId !== currUserId) 
        await this.fetchUserById(currUserId)
      
      const data = 
        clientPhone: this.user.phoneNumber,
        comment: this.comment,
        clientReferenceNumber: this.user.clientNumber,
        domain: this.taskType
      
      await this.addNewTask(data)
      if (this.user.clientNumber !== null) 
        const filters =  clientReferenceNumber:  value: this.user.clientNumber  
        this.tasks = await this.fetchTasks( filters )
        // this.tasks may be useless here
      
      console.log(this.tasks)
      this.$nextTick(() =>  this.$bvModal.hide('addTaskModal') )
    ,
    checkFormValidity () 
      const valid = this.$refs.form.checkValidity()
      this.commentState = valid
      this.taskTypeState = valid
      return valid
    ,
    resetModal () 
      this.comment = ''
      this.commentState = null
      this.taskTypeState = null
    
  

</script>

当我添加一个任务时,我调用 getalltasks 来改变存储,以便添加所有任务。然后我想渲染它们。它们已呈现,但最后一个任务上的 createdOn 属性是 InvalidDate ,当我在控制台记录时它是未定义的。

我需要在模态中再次调用 gettasks 的原因是添加任务的响应没有返回属性 createdOn。我不想在前端设置,我想从数据库中获取。

我登录了商店,所有的任务都添加到了商店中。

为什么我的父组件没有渲染这个特定的 createdOn 属性?

如果我刷新页面,一切都会正常显示。

【问题讨论】:

【参考方案1】:

如果您将任何内容添加到 v-for 显示的项目列表中,则必须设置唯一键。根据您的解释,我假设您的键是索引,当您添加新项目时,您会弄乱当前索引。键必须是唯一且不可更改的。您需要做的是为每个元素创建一个唯一的 id。


  id: Math.floor(Math.random() * 10000000)

当你创建一个新任务时,使用相同的代码生成一个新的id,并使用id作为key。如果这没有帮助,请分享您的 d-table 和相关的 vuex 代码。

【讨论】:

我看不出这与此有什么关系。添加了元素,但日期只是搞砸了。它与 id 无关。 ID 是来自数据库的随机 guid。

以上是关于Vue.js:子组件改变状态,父组件显示属性未定义的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 子组件未更新

Vue js 子组件中未定义的道具。但仅在其脚本中

VUe.js 父组件向子组件中传值及方法

数据未从 Vue.js 中的子组件发送到父组件

Vue.js:根据子组件的状态禁用父组件上的按钮

Vue.js2.0中子组件修改父组件传递过来的props,并不影响父组件的原始数据