Search code examples
vue.jsdevextreme

The object properties passed to modal dialog from the list don't display using Vue 3 (Composition API)


I have Vue 2 app (Options API, DevExtreme lib) and want to migrate to Vue 3 (Composition API, DevExtreme lib). As first step I successfully migrated to Vue 3 (Options API, DevExtreme lib). But, there are some issues when I try to migrate to Composition API.

Regarding concrete case there's a list of items and when you click particular one, the modal dialog opens up (showModal method that accepts object with column's values). There are appropriate fields on modal dialog that should be filled out with object's properties. But, each of those fields is empty.

The code looks as following:

1) List of items:

<template>
  <div>
    <v-data-table
      :data-source="reqtypes"
      key-expr="fbReqtypeid"
      :columns="columns"
      @row-dbl-click="rowDblClick"
      :ref="dataGridRefKey"
    >
      ...
    </v-data-table>

    <v-app-types-modal ref="appTypesModal" @ok="onOk" />
  </div>
</template>

<script>
...
export default {
  ...
  setup() {
    const appTypesModal = ref(null)
    const isLoading = ref(false)
    const reqtypes = reactive([
      {
        fbReqtypeid: "1",
        listItem: 'aaaa',
        type: "AAAA",
        link: "https://yandex.ru",
        digits: '1234',
        typeName: "Type A",
        genericName: "Generic A",
        singleLineText: "https://yandex.ru",
        multiLineText:
          "Japan is a sovereign island nation in East Asia. Located in the Pacific Ocean, it lies off the eastern coast of the Asian mainland and stretches from the Sea of Okhotsk in the north to the East China Sea and China in the southwest.",
      },
      {
        fbReqtypeid: "2",
        listItem: 'bbbb',
        type: "BBBB",
        link: "https://stackoverflow.com",
        digits: '5678',
        typeName: "Type B",
        genericName: "Generic B",
        singleLineText:
          "Japan is a sovereign island nation in East Asia.",
        multiLineText:
          "Japan is a sovereign island nation in East Asia. Located in the Pacific Ocean, it lies off the eastern coast of the Asian mainland and stretches from the Sea of Okhotsk in the north to the East China Sea and China in the southwest. The Greater Tokyo Area is the most populous metropolitan area in the world.",
      },
    ])
    const columns = reactive([
      { dataField: "fbReqtypeid", caption: "ID", dataType: "string", visible: false },
      { dataField: "listItem", caption: "List item", dataType: "string" },
      { dataField: "type", caption: "Тип заявки", dataType: "string", cellTemplate: "link-to-modal" },
      { dataField: "link", caption: "Ссылка", dataType: "string" },
      { dataField: "digits", caption: "Цифры", dataType: "string" },
      { dataField: "typeName", caption: "Наименование типа заявки", dataType: "string" },
      { dataField: "genericName", caption: "Generic name", dataType: "string" },
      { dataField: "singleLineText", caption: "Single line", dataType: "string" },
      { dataField: "multiLineText", caption: "Multi line", dataType: "string" },
    ])
    const dataGridRefKey = ref('my-data-grid')

    function rowDblClick(e) {
      showModal(e.data);
    }
    function showModal(selectedReqtype) {
      appTypesModal.value.showModal(selectedReqtype);
    }
    ...
}
</script>

2) The modal dialog:

<template>
  <v-modal-dialog
    :visible="isModalVisible"
    title="CONNECTION"
    okText="Save"
    cancelText="Cancel"
    :isSaveDisabled="isDisabled"
    :width="550"
    :height="780"
    @ok="onOk"
    @cancel="onCancel"
    @hiding="onHiding"
  >
    <DxForm
      :form-data="selectedItem"
      label-mode="static"
      validation-group="selectedData"
    >
      <DxItem template="type" :is-required="true" />
      <template #type>
        <v-latin-letters-and-digits-text-box
          :modelValue="selectedItem.type"
          @update:modelValue="($event) => {selectedItem.type = $event}"
          label="Type"
          label-mode="static"
        >
          <DxValidator validation-group="selectedData">
            <DxRequiredRule message="Required field: Type." />
          </DxValidator>
        </v-latin-letters-and-digits-text-box>
      </template>

      <DxItem template="list" :is-required="true" />
      <template #list>
        <v-select-box
          :modelValue="selectedItem.listItem"
          :data-source="list"
          @update:modelValue="($event) => {selectedItem.listItem = $event}"
          label="List"
          label-mode="static"
          placeholder="Select..."
          value-expr="listItem"
          display-expr="listItem"
          drop-down-button-template="imageIcons"
          :show-clear-button="true"
        >
          <template #imageIcons="{}">
            <div class="set-icons">
              <img src="@/assets/custom-dropbutton-icon.svg" class="custom-icon" />
            </div>
          </template>
          <DxValidator validation-group="selectedData">
            <DxRequiredRule message="Required field: List item." />
          </DxValidator>
        </v-select-box>
      </template>
      ...
    </DxForm>
  </v-modal-dialog>
</template>

<script>
export default {
  emits: ['ok'],
  setup(_, { emit }) {
    let isModalVisible = ref(false)
    const isScrollingEnabled = ref(true)
    let selectedItem = reactive({
      fbReqtypeid: '',
      listItem: '',
      type: '',
      link: '',
      digits: '',
      typeName: '',
      genericName: '',
      singleLineText: '',
      multiLineText: ''
    })
    let linkContent = ref('Link to page')
    const patternName = ref(/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/)
    let isValidLink = ref(false)
    const list = reactive([
      { id: 1, listItem: 'aaaa' },
      { id: 2, listItem: 'bbbb' },
      { id: 3, listItem: 'cccc' },
    ])

    const isDisabled = computed(() => {
      if (!selectedItem.type || !selectedItem.typeName) {
        return true;
      }
      return false;
    })

    function showModal(item) {
      selectedItem = cloneDeep(item);
      isModalVisible.value = true;
    }
    ...
}
</script>

I'm not sure what I'm doing wrong, but suppose there's something connected with new reactivity model in Vue 3.


Solution

  • In your modal, you are declaring selectedItem as a reactive object, references in the template are bound to this object:

    let selectedItem = reactive({...})
    

    Then you assign a new object to the variable:

    function showModal(item) {
      selectedItem = cloneDeep(item);
    }
    

    But this only affects the variable in your script, the template references still point to the initial value of selectedItem.


    Instead, declare selectedItem as a ref:

    const selectedItem = ref({...})
    

    and then assign to the ref's value:

    function showModal(item) {
      selectedItem.value = cloneDeep(item);
    }
    

    You'll have to adjust other access to the variable in your script (but not in template):

    const isDisabled = computed(() => {
      if (!selectedItem.value.type || !selectedItem.value.typeName) {
        ...
    

    Alternatively, you can use reactive(), but nest selectedItem:

    const data = reactive({
      selectedItem: {...}
    })
    

    and

    function showModal(item) {
      data.selectedItem = cloneDeep(item);
    }