How to dynamically add vue bootstrap dropwdown list item?

Viewed 5783

I'm trying to dynamically add drowdown list items using bootstrap-vue, and I'm having trouble getting the dropdown list items to render to the browser. Here's what I have so far, any input would be much appreciated. I want it to be dynamics so that if the original JSON ever changes the dropwdown list items will change with it without having to go back and change the code.

NOTE: the reportData is a list of objects, which is why I'm setting the first element of the reportData to the newData that i want the dropdown list items to be rendered from.

Dropdown List Component:

  <section class="drop-down">
    <div>
      <b-dropdown id="dropdown-list" text="Error Reports" class="m-md-2">
       <b-dropdown-item >ITEM</b-dropdown-item>
        <b-dropdown-divider></b-dropdown-divider>
      </b-dropdown>
    </div>

    <button @click="changeReport">Click to change</button>
  </section>
</template>

<script lang="js">

  export default  {
    name: 'drop-down',
    props:['reportData'],
    data () {
      return {
        newData: this.reportData[0]
      }
    },
    methods: {
      changeReport(){
        this.newData = this.reporData[1]
      }
    },
    watch: {
      newData : function(val, OldVal){
        var dropdownList = document.getElementById("dropdown-list")
          for (var key in val){
            console.log(key)
            var dropdownItem = document.createElement('b-dropdown-item')
            dropdownItem.value = key
            dropdownList.add(drowdownItem, 0)
          }
      }
    }
}


</script>

<style>

</style>
2 Answers

Try to avoid directly manipulating the DOM when you can while using Vue. In this case, your best option would be to set the currently active data to whatever list you want to display. Then, in the template, loop though each of those active data items.

new Vue({
  el: "#app",
  data: {
    reportData: [{
      text: 'Item 1 Text',
      value: 'Item 1 Value'
    }, {
      text: 'Item 2 Text',
      value: 'Item 2 Value'
    }, {
      text: 'Item 3 Text',
      value: 'Item 3 Value'
    }],
  }
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />

<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>

<div id="app">
  <section class="drop-down">
    <b-dropdown text="Error Reports" class="m-md-2">
      <b-dropdown-item v-for="(item, key) in reportData" :key="key">
        {{ item.text }}: {{ item.value }}
      </b-dropdown-item>
    </b-dropdown>
  </section>
</div>

Feel free to let me know if you have any questions.


Edit:

The following example may help clarify what you need to do if you have nested arrays of objects that you know the structure of ahead of time.

However, if you don't know the depth of your tree, you may have to consider using recursive components.

new Vue({
  el: "#app",
  data: {
    // This is your input data
    dataStore: [{
      name: 'Item 1',
      value: [{
        text: 'SubItem 1 Text',
        value: 'SubItem 1 Value'
      }, {
        text: 'SubItem 2 Text',
        value: 'SubItem 2 Value'
      }, {
        text: 'SubItem 3 Text',
        value: 'SubItem 3 Value'
      }]
    }, {
      name: 'Item 2',
      value: [{
        text: 'SubItem 1 Text',
        value: 'SubItem 1 Value'
      }, {
        text: 'SubItem 2 Text',
        value: 'SubItem 2 Value'
      }, {
        text: 'SubItem 3 Text',
        value: 'SubItem 3 Value'
      }, {
        text: 'SubItem 4 Text',
        value: 'SubItem 4 Value'
      }, {
        text: 'SubItem 5 Text',
        value: 'SubItem 5 Value'
      }, ]
    }],

    // This is the data we will display
    activeData: null
  },

  created() {
    // The value that will be selected upon creation
    this.activeData = this.dataStore[0]
  },

  methods: {
    changeData() {
      this.activeData = this.dataStore[1]
    }
  }
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />

<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>

<div id="app">
  <section v-if="activeData" class="drop-down">
    <h2>{{ activeData.name }}</h2>
    <b-dropdown text="Error Reports" class="m-md-2">
      <b-dropdown-item v-for="(item, key) in activeData.value" :key="key">
        {{ item.text }}: {{ item.value }}
      </b-dropdown-item>
    </b-dropdown>
  </section>

  <button @click="changeData">Change Data Item</button>
</div>

try this

      <b-dropdown id="dropdown-list" text="Error Reports" class="m-md-2">
       <b-dropdown-item v-for="item in listItems">@{{ item }}</b-dropdown-item>
        <b-dropdown-divider></b-dropdown-divider>
      </b-dropdown>

      <button @changeReport>Click To Change</button>

In your JS....

    data() {
      items: []
    },

    computed: {
      listItems() {
        return this.items;
      }
    },
    methods: {
      changeReport(){
        this.items = this.reporData[1]
      }

Something like that. I don't know exactly what your data looks like, but if you fill this.list with your new data, the computed property will notice that change and re-render your dropdown.

Related