2

I am developing a Vuejs application within which I have a field. For this field, users can provide the values and this field expands dynamically based on the user-provided values.

The field name is extensions, initially an Add Extension button will be displayed. With on click of the button, a bootstrap modal will be displayed which has 3 fields: namespace (text), localname (text), datatype(dropdown: string/complex). If the datatype is string then a simple text field will be displayed. However, if the datatype is complex then another button should be displayed and with on click of the button again the same modal is displayed with fields and the process continues. So the created JSON based on this will expand horizontally and vertically.

I am able to complete the first iteration and display the elements to users on the front end. However, for further iteration, I am not understanding how to achieve it using the recursive approach. Since I don't know how many times users may create the extensions I need to have an approach that dynamically does this.

Can someone please help me how to create and display the JSONarray using Vuejs which expands horizontally and vertically?

Following is the code I have so far:

<template>
  <div class="container-fluid">
    <div class="row">
      <div class="col-md-3">
        <span>Extensions</span>
        <button class="form-control" @click="createExtensions()">
          Add Another
        </button>
      </div>
    </div>

    <div v-for="extension in extensionList" :key="extension.ID" class="form-inline">
      <span>{{ extension.namespace+ ":"+extension.localName }}</span>
      <input v-if="extension.dataType == 'string'" type="text" @input="AddExtensionText($event,extension.ID)">
      <button v-if="extension.dataType == 'complex'" @click="AddComplextExtension(extension.ID)">
        Add another
      </button>
    </div>

    <b-modal
      id="Extension"
      title="Add Another Element"
      size="lg"
      width="100%"
      :visible="showModal"
    >
      <b-form id="AddExtension" @submit.prevent="submitExtension">
        <div class="form-group">
          <label for="message-text" class="col-form-label">Namespace URI:</label>
          <input
            v-model="extension.namespace"
            type="text"
            class="form-control"
            required
          >
        </div>
        <div class="form-group">
          <label for="message-text" class="col-form-label">Local Name:</label>
          <input
            v-model="extension.localName"
            type="text"
            class="form-control"
            required
          >
        </div>
        <div class="form-group">
          <label for="AddExtensionDataType" class="col-form-label">Data Type:</label>
          <b-form-select v-model="extension.dataType" class="form-control">
            <b-form-select-option value="string">
              String
            </b-form-select-option>
            <b-form-select-option value="complex">
              Complex
            </b-form-select-option>
          </b-form-select>
        </div>
      </b-form>
      <template #modal-footer="{ cancel }">
        <b-btn @click="cancel">
          Cancel
        </b-btn>
        <b-btn variant="primary" type="submit" form="AddExtension">
          OK
        </b-btn>
      </template>
    </b-modal>
  </div>
</template>

<script>

export default {
  data () {
    return {
      extensionList: [],
      extensionCount: 0,
      extension: {
        namespace: '',
        localName: '',
        dataType: 'string'
      },
      showModal: false
    }
  },
  methods: {
    // Method to create extensions and add
    createExtensions () {
      this.showModal = true
    },

    // Function to submit the each extension
    submitExtension () {
      this.showModal = false
      const extensionObj = {}
      extensionObj.ID = this.extensionCount
      extensionObj.namespace = this.extension.namespace
      extensionObj.localName = this.extension.localName
      extensionObj.dataType = this.extension.dataType
      this.extensionList.push(extensionObj)
      this.extensionCount++
    },

    // On addition of the input field value update List
    AddExtensionText (event, extensionID) {
      const extension = this.extensionList.filter(ex => ex.ID === extensionID)[0]
      if (typeof extension !== 'undefined') {
        extension.text = (typeof event.target.value !== 'undefined') ? event.target.value : ''
      }
    },

    // Add complex extension
    AddComplextExtension (extensionID) {
      this.showModal = true
    }
  }
}
</script>

<style>

</style>

This is the initial field I have:

enter image description here

This is what I want to achieve where everything is created dynamically and JSON expands both horizontally and vertically:

enter image description here

Can someone please let me know how to create such dynamic JSON using Vuejs and display the same in the frontend.

1

1 Answer 1

1

To display data recursively, you need to use recursive components.

Abstract your v-for code into another component file (let's call it NodeComponent.vue). Pass your extensionList to this component, then inside this component, add another NodeComponent for each extension which has type complex.

Since your extension would be another array if it is complex, you can pass it directly into this NodeComponent as a prop and let recursion work its magic.

NodeComponent.vue

<template>
  <div>
    <div
      v-for="extension in extensionList"
      :key="extension.ID"
      class="form-inline"
    >
      <span>{{ extension.namespace + ":" + extension.localName }}</span>
      <input
        v-if="extension.dataType == 'string'"
        type="text"
        @input="$emit('AddExtensionText', {$event, id: extension.ID}"
      />
      <NodeComponent v-if="extention.dataType == 'complex'" :extentionList="extension" @AddExtensionText="AddExtensionText($event)"/>
      <button
        v-if="extension.dataType == 'complex'"
        @click="AddComplextExtension(extension.ID)"
      >
        Add another
      </button>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    extensionList: Array,
    extension: Object,
  },
  methods: {
    AddComplextExtension(extensionID) {
      // Emit event on root to show modal, or use this.$bvModal.show('modal-id') or create a dynamic modal, see: https://bootstrap-vue.org/docs/components/modal#message-box-advanced-usage
    }
    AddExtensionText({ value, id }) {
      const i = this.extensionList.findIndex((el) => el.ID === id);
      this.$set(extensionList, i, value);
    }
  }
};
</script>

Note that I emit a custom event from child NodeComponents on changing input text so that the parent can make this change in its extensionList array, using this.$set to maintain reactivity.

EDIT: If you want to add new Node components:

You need to have a parent component that holds the first NodeComponent in it. In here you'll define the modal (if you define it inside NodeComponent, you'll have a separate modal reference for each NodeComponent. Judging from your code you're probably using Bootstrap-Vue, it injects modals lazily when shown, so I don't think this will affect your performance too much, but it still doesn't feel like good code.). You need to emit event on root to show the modal. You need to send the extensionList as payload with this event like this: this.$root.emit('showModal', extensionList). In you parent component you can listen to the event and show the modal. Now inside your submitExtension function, you can use this extensionList and push a new object to it. The corresponding NodeComponent will update itself since arrays are passed by reference.

this.$root.on('showModal`, (extensionList) => {
    this.editExtensionList = extensionList;
    showModal = true;
}
    submitExtension() {
      this.showModal = false
      const extensionObj = {}
      extensionObj.ID = this.extensionCount
      extensionObj.namespace = this.extension.namespace
      extensionObj.localName = this.extension.localName
      extensionObj.dataType = this.extension.dataType
      this.editExtensionList.push(extensionObj)
      this.extensionCount++
    }

All being said, at this point it might be worthwhile to invest in implementing a VueX store where you have a global extentionList and define mutations to it.

Sign up to request clarification or add additional context in comments.

9 Comments

@BATMAN_2008 Check my updated answer
Cool that you made a sandbox, makes things a lot easier. I'll fork it post a link of working sandbox in a while.
@BATMAN_2008 You were quite close. But you made it more complicated than it had to be. Here's the working sandbox. Let me know if you want clarification on something.
You are awesome, thanks a lot for your response. Everything is working as expected. I have added the modify, deletion, and capturing of the text field value based on changes within my application. Everything seems to work as expected. Thanks again for helping me with the issue. Have a great day :)
Always glad to help, great day to you too :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.