计算属性发生更改时,Vue父组件不会重新呈现

时间:2020-03-03 22:22:26

标签: vuejs2 vue-reactivity

我有一个User-Select组件,它包装了一个v-select。此组件的工作是获取作为用户类型的用户列表,并允许用户选择一个或多个用户。 这是该组件的代码。

<template>
  <div>
    <v-select
      id="recipients"
      name="recipients"
      :options="options"
      label="name"
      multiple
      @search="onSearch"
      v-model="selectedVal"
    />
  </div>
</template>

<script>
import vSelect from 'vue-select'
import axios from 'axios'
import _ from 'lodash'
export default {
  name: 'UserSelect',
  components: {
    'v-select': vSelect
  },
  props: {
    value: {
      type: Array
    }
  },
  data() {
    return {
      options: []
    }
  },
  computed: {
    selectedVal: {
      get() {
        return this.value
      },
      set(val) {
        //this.value = val
        this.$emit('input', val)
      }
    }
  },
  methods: {
    onSearch(search, loading) {
      loading(true)
      this.search(loading, search, this)
    },
    setSelected: function(val) {
      this.$emit('input', val)
    },
    search: _.debounce((loading, search, vm) => {
      axios
        .post('searchPeople', { search }, { useCredentails: true })
        .then(res => {
          vm.options = res.data
          loading(false)
        })
    }, 350)
  }
}
</script>

<style lang="scss" scoped>
@import url('https://unpkg.com/vue-select@latest/dist/vue-select.css');
</style>

如您所见,我有一个链接到计算属性的v模型,该属性发出输入事件。我的财产名称也是value。因此,我希望使用该UserEvent组件的父组件能够进行v-model。

Edit elastic-fast-t3pti

在父组件中,我具有一个已计算的属性,我已对该属性进行了v建模。这是代码。

<template>
  <div>
    <b-modal id="editMessage" :title="title" :static="true">
      <form id="newMessageForm" class="row">
        <div class="col-md-12">
          <div class="form-group row">
            <label for="to" class="col-sm-3 col-form-label">To:</label>
            <user-select
              class="col-sm-7"
              style="padding-left: 0px; padding-right: 0px"
              v-model="editedMessage.recipients"
            />
          </div>
          <div class="form-group row">
            <label for="subject" class="col-sm-3 col-form-label"
              >Subject:</label
            >
            <input
              id="subject"
              name="subject"
              type="text"
              class="form-control col-sm-7"
              :value="editedMessage.messageSubject"
            />
          </div>
          <div class="form-group row">
            <label for="date" class="col-sm-3 col-form-label"
              >Schedule for later :
            </label>
            <input
              type="checkbox"
              class="form-control col-sm-7"
              v-model="scheduleForLater"
              id="scheduleCheckBox"
            />
          </div>
          <div class="form-group row" v-if="scheduleForLater">
            <label for="date" class="col-sm-3 col-form-label"
              >Scheduled Date:</label
            >
            <datetime
              v-model="editedMessage.sentDate"
              type="datetime"
              class="col-sm-17"
              input-class="form-control col-sm-15"
              input-id="date"
            />
          </div>
          <div class="form-group row">
            <label for="body" class="col-sm-3 col-form-label">Message:</label>
            <textarea
              id="body"
              name="body"
              type="text"
              rows="10"
              class="form-control col-sm-7"
              :value="editedMessage.messageBody"
            ></textarea>
          </div>
        </div>
      </form>

      <template v-slot:modal-footer="{ hide }">
        <!-- Emulate built in modal footer ok and cancel button actions -->

        <b-button size="sm" variant="light" @click="hide()">
          Cancel
        </b-button>
        <b-button
          size="sm"
          variant="secondary"
          @click="
            sendMessage(true)
            hide()
          "
        >
          Save Draft
        </b-button>
        <b-button
          size="sm"
          variant="primary"
          @click="
            sendMessage(false)
            hide()
          "
        >
          Send
        </b-button>
      </template>
    </b-modal>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import UserSelect from '@/components/UserSelect.vue'
import axios from 'axios'
export default {
  name: 'NewMessage',
  components: {
    'user-select': UserSelect
  },
  data() {
    return {
      options: [],
      scheduleForLater: false
    }
  },
  mounted() {},
  computed: {
    ...mapState({
      openMessage: state => state.message.openMessage,
      messageAction: state => state.message.messageAction
    }),
    editedMessage: {
      get() {
        if (this.messageAction === 'new') {
          return this.newMessage()
        } else if (this.messageAction === 'reply') {
          let openMessageClone = Object.assign({}, this.openMessage)
          // make sender as the recipient.
          return Object.assign(openMessageClone, {
            messageSubject: 'RE: ' + this.openMessage.messageSubject,
            recipients: [
              {
                name: this.openMessage.sender.name,
                id: this.openMessage.sender.id
              }
            ]
          })
        } else {
          let openMessageClone = Object.assign({}, this.openMessage)
          return Object.assign(openMessageClone, {
            messageSubject: 'FW: ' + this.openMessage.messageSubject
          })
        }
      },
      set(val) {
        this.$emit('input', val)
      }
    },
    title() {
      if (this.messageAction === 'new') {
        return 'New'
      } else if (this.messageAction === 'reply') {
        return 'Reply'
      } else {
        return 'Forward'
      }
    }
  },
  methods: {
    newMessage() {
      return {
        messageBody: '',
        messageSubject: '',
        recipients: [],
        sender: {}
      }
    },
    sendMessage(saveOrUpdateDraft) {
      var url = ''
      var message = {
        recipients: this.editedMessage.recipients.map(x => x.id),
        subject: this.editedMessage.messageSubject,
        body: this.editedMessage.messageBody,
        sentDate: this.scheduleForLater ? this.editedMessage.sentDate : null,
        id: ['editDraft', 'viewScheduled'].includes(this.messageAction)
          ? this.editedMessage.messageId
          : null
      }
      // id indiciates message id of draft message if one was opened.
      if (saveOrUpdateDraft) {
        // if no changes have been made to an opened draft, or the draft is completely empty for a new or existing draft , just go back.
        if (message.id) {
          url = `updateDraft`
        } else {
          url = 'saveNewDraft'
        }
      } else {
        if (message.id) {
          url = `sendSavedMessage`
        } else {
          url = 'sendMessage'
        }
      }
      axios
        .post(`eventdirect/${url}`, message, {
          withCredentials: true
        })
        .then(response => {
          if (url.includes('Draft') && this.messageAction === 'viewScheduled') {
            this.$store.dispatch('sent/moveToDraft', response.data)
          } else if (url.includes('Draft')) {
            this.$store.dispatch('drafts/updateDraft', response.data)
          } else {
            // else part is sending a message or scheduling a draft.
            if (this.messageAction === 'editDraft') {
              this.$store.dispatch('drafts/deleteDraft', response.data)
            }
            // if we are sending an existing scheduled messsage , just update the sent vuex store , so that the message moves from scheduled to sent bucket.
            if (this.messageAction === 'viewScheduled') {
              this.$store.dispatch('sent/updateMessage', response.data)
            } else {
              this.$store.dispatch('sent/addSentItem', response.data)
            }
          }
        })
        .catch(() => {
          // TODO , add a qtip here to notify user , this message should be sent later.
          // messages in draft store with target , should be posted to the target
          this.$store.dispatch('drafts/updateDraft', {
            ...message,
            target: url
          })
        })
    }
  }
}
</script>

现在我可以在vue开发工具中看到此NewMessage组件中的计算值已更改。但是,此组件不会重新渲染,并且所选的值不会向下传递到UserSelect组件,直到我切换,计划稍后的复选框,这会导致组件数据发生更改,并触发Vue组件突然开始显示选择的值。

这是怎么回事。在这里我无法理解Vue的反应性。

感谢您的期待。您可以here尝试一下,也可以单击上方的“编辑沙盒”按钮来查看和编辑代码。

要尝试,请点击Vue消息链接,然后点击回复,然后输入“测试”,然后从下拉列表中选择“测试用户”。直到您单击复选框“以后安排”,所选用户才会显示。

PS:在组件UserSelect内,在计算属性selectedVal的设置器中,如果我手动设置属性value的值(通过简单地取消注释第39行-现在对此进行注释),一切正常。但是,我收到警告,不应直接设置属性。由于我发出了input事件,因此父组件应更改其v模型并重新渲染,从而导致子组件重新渲染。如果我的理解是错误的,请纠正我。问题再次出在,父组件的v模型发生了变化,但是它没有重新渲染。

enter image description here

0 个答案:

没有答案