Customize ListView with dynamic tags in Vue ListView component

21 Feb 202522 minutes to read

You can customize the ListView items using the template property. Here, the dynamic tags are added and removed in the list item from another ListView. Refer to the following steps to achieve this.

*Initialize a dynamic ListView with the required property that holds the tags of the parent ListView, and bind the select event (which triggers when a list item is selected), in which you can get and add the selected item value as tags into the parent ListView. Refer to the following code sample.

   //Select the event that is is rendered inside dialog for ListView
   addTag: function(e) {
    let listTag = document.createElement('span');
    listTag.className = 'advanced-option';
    let labelElem = document.createElement('span');
    labelElem.className = 'label';
    let deleteElem = document.createElement('span');
    deleteElem.className = 'delete';
    deleteElem.onclick = this.removeTag;
    labelElem.innerHTML = e.target.textContent;
    listTag.appendChild(labelElem);
    listTag.appendChild(deleteElem);
    let tag = document.createElement('span');
    tag.className = 'advanced-option-list';
    tag.appendChild(listTag);
    this.$refs.listviewInstance.$el.ej2_instances[0].element.querySelector('.e-active').appendChild(tag);
    }
  • Render the dialog component with empty content and append the created dynamic ListView object to the dialog in the created event.

  • Bind the click event for the button icon (+) to update the ListView data source with tags and open the dialog with this dynamic ListView.

      //Method to hide/show the dialog and update the ListView data source
       renderDialog: function(id){
         if (document.getElementsByClassName('e-popup-open').length !== 0) {
          this.$refs.dialogInstance.hide();
        }
        else {
          this.listObj = document.getElementById('dialog').querySelector("#list").ej2_instances[0];
          this.listObj.dataSource = this.datasource[id];
          this.listObj.dataBind();
          this.$refs.dialogInstance.position = { X: document.querySelector('.e-add-icon').getBoundingClientRect().left + 50, Y: document.querySelector('.e-add-icon').getBoundingClientRect().top - 5 };
          this.$refs.dialogInstance.show();
        }
      }
  • Bind the click event to the added dynamic tags to remove them. Refer to the following code sample.

     //Method to remove the list item
      removeTag() {
      this.$refs.listviewInstance.$el.ej2_instances[0].selectedLI.children[1].remove();
     }
<template>
  <div id="sample">
    <ejs-listview ref='listviewInstance' id='templatelist' :dataSource='data' :fields='fields' width='350'
      template='listTemplate'>
      <template v-slot:listTemplate="{ data }">
        <div><span class="templatetext"> </span> <span class="designationstyle"><ejs-button :id='data.Id'
              class="e-but" iconCss='e-icons e-add-icon' cssClass='e-small e-round'></ejs-button></span></div>
      </template>
    </ejs-listview>
    <ejs-dialog id='dialog' ref='dialogInstance' width='200px' content='dialogcontent' :animationSettings='animation'
      :visible='visible' showCloseIcon='true' :position='position'>
      <template v-slot:dialogcontent>
        <ejs-listview id="list" :dataSource="datasource.Brooke" :select="addTag" :fields="fields" showHeader=true
          headerTitle='Favorite' width='200px'></ejs-listview>
      </template>
    </ejs-dialog>
  </div>
</template>

<script setup>

import { ListViewComponent as EjsListview } from "@syncfusion/ej2-vue-lists";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
import { DialogComponent as EjsDialog } from "@syncfusion/ej2-vue-popups";
import { onMounted, onBeforeMount, ref } from "vue";

const listviewInstance = ref(null);
const dialogInstance = ref(null);

const data = [{ "Id": "Brooke", "Name": "Brooke" },
{ "Id": "Claire", "Name": "Claire" },
{ "Id": "Erik", "Name": "Erik" },
{ "Id": "Grace", "Name": "Grace" },
{ "Id": "Jacob", "Name": "Jacob" }];
const fields = { text: 'Name' };
const brookeTag = [{ "id": "list11", "Name": "Discover Music" },
{ "id": "list12", "Name": "Sales and Events" },
{ "id": "list13", "Name": "Categories" },
{ "id": "list14", "Name": "MP3 Albums" },
{ "id": "list15", "Name": "More in Music" },
];
const claireTag = [{ "id": "list21", "Name": "Songs" },
{ "id": "list22", "Name": "Bestselling Albums" },
{ "id": "list23", "Name": "New Releases" },
{ "id": "list24", "Name": "Bestselling Songs" },
];
const erikTag = [{ "id": "list31", "Name": "Artwork" },
{ "id": "list32", "Name": "Abstract" },
{ "id": "list33", "Name": "Acrylic Mediums" },
{ "id": "list34", "Name": "Creative Acrylic" },
{ "id": "list35", "Name": "Canvas Art" }
];
const graceTag = [{ "id": "list41", "Name": "Rock" },
{ "id": "list42", "Name": "Gospel" },
{ "id": "list43", "Name": "Latin Music" },
{ "id": "list44", "Name": "Jazz" },
];
const jacobTag = [{ "id": "list51", "Name": "100 Albums - $5 Each" },
{ "id": "list52", "Name": "Hip-Hop and R&B Sale" },
{ "id": "list53", "Name": "CD Deals" }
];
const visible = false;
let datasource = {};
const animation = { effect: 'None' };
let position = {};

onBeforeMount(() => {
  datasource = { "Brooke": brookeTag, "Claire": claireTag, "Erik": erikTag, "Grace": graceTag, "Jacob": jacobTag };
});
onMounted(() => {
  for (let i = 0; i < data.length; i++) {
    document.getElementById(data[i].Id).addEventListener("click", (e) => {
      renderDialog(e.currentTarget.id);
    });
  }
});

const renderDialog = (id) => {
  if (document.getElementsByClassName('e-popup-open').length !== 0) {
    dialogInstance.value.hide();
  }
  else {
    let listObj = document.getElementById('dialog').querySelector("#list").ej2_instances[0];
    listObj.dataSource = datasource[id];
    listObj.dataBind();
    dialogInstance.value.ej2Instances.position = { X: document.querySelector('.e-add-icon').getBoundingClientRect().left + 50, Y: document.querySelector('.e-add-icon').getBoundingClientRect().top - 5 };
    dialogInstance.value.show();
  }
};
const addTag = (e) => {
  let listTag = document.createElement('span');
  listTag.className = 'advanced-option';
  let labelElem = document.createElement('span');
  labelElem.className = 'label';
  let deleteElem = document.createElement('span');
  deleteElem.className = 'delete';
  deleteElem.onclick = removeTag;
  labelElem.innerHTML = e.text;
  listTag.appendChild(labelElem);
  listTag.appendChild(deleteElem);
  let tag = document.createElement('span');
  tag.className = 'advanced-option-list';
  tag.appendChild(listTag);
  listviewInstance.value.$el.ej2_instances[0].element.querySelector('.e-active').appendChild(tag);
};
const removeTag = () => {
  listviewInstance.value.$el.ej2_instances[0].selectedLI.children[1].remove();
};

</script>

<style>
#sample {
  padding: 40px;
}

.advanced-option-list {
  display: inline-flex;
  list-style: none;
  margin: 0;
  padding: 0;
  margin-top: 4px;
  overflow: auto;
}

.advanced-option {
  border: 1px solid #0078d7;
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
  padding: 0 20px 0 5px;
  margin: 0 3px 1px 0;
  float: left;
  position: relative;
  background: white;
  line-height: 21px;
}

.advanced-option .label {
  color: #0078d7;
  font-size: 10px;
}

.advanced-option .delete {
  background: url(https://research.isg-one.com/Assets/img/Content/icn/tiny-del.png) right center no-repeat;
  padding-left: 10px;
  padding-top: 2px;
  right: 0;
  position: absolute;
  top: 1px;
  cursor: pointer;
  height: 16px;
  width: 30px;
}

.designationstyle {
  float: right;
  position: relative;
  right: 10px;
}

.cont-bg {
  font-size: 17px;
  height: 46px;
  padding-top: 10px;
  width: 100%;
}

.designationstyle .e-add-icon::before {
  content: '\e823';
}

#sample .e-list-item {
  height: auto;
}

.e-dialog .e-dlg-header-content+.e-dlg-content {
  margin-top: -18px;
}
</style>
[Yesterday 4:03 PM] Vishnu Moorthi
<template>
  <div id="sample">
    <ejs-listview ref='listviewInstance' id='templatelist' :dataSource='data' :fields='fields' width='350'
      template='listTemplate'>
      <template v-slot:listTemplate="{ data }">
        <div><span class="templatetext"> </span> <span class="designationstyle"><ejs-button :id='data.Id'
              class="e-but" iconCss='e-icons e-add-icon' cssClass='e-small e-round'></ejs-button></span></div>
      </template>
    </ejs-listview>
    <ejs-dialog id='dialog' ref='dialogInstance' width='200px' content='dialogcontent' :animationSettings='animation'
      :visible='visible' showCloseIcon='true' :position='position'>
      <template v-slot:dialogcontent>
        <ejs-listview id="list" :dataSource="datasource.Brooke" :select="addTag" :fields="fields" showHeader=true
          headerTitle='Favorite' width='200px'></ejs-listview>
      </template>
    </ejs-dialog>
  </div>
</template>

<script>

import { ListViewComponent } from "@syncfusion/ej2-vue-lists";
import { DialogComponent } from "@syncfusion/ej2-vue-popups";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";

export default {
  name: "App",
  components: {
    "ejs-listview": ListViewComponent,
    "ejs-button": ButtonComponent,
    "ejs-dialog": DialogComponent
  },
  data: function () {
    return {
      data: [{ "Id": "Brooke", "Name": "Brooke" },
      { "Id": "Claire", "Name": "Claire" },
      { "Id": "Erik", "Name": "Erik" },
      { "Id": "Grace", "Name": "Grace" },
      { "Id": "Jacob", "Name": "Jacob" }],
      fields: { text: 'Name' },
      brookeTag: [{ "id": "list11", "Name": "Discover Music" },
      { "id": "list12", "Name": "Sales and Events" },
      { "id": "list13", "Name": "Categories" },
      { "id": "list14", "Name": "MP3 Albums" },
      { "id": "list15", "Name": "More in Music" },
      ],
      claireTag: [{ "id": "list21", "Name": "Songs" },
      { "id": "list22", "Name": "Bestselling Albums" },
      { "id": "list23", "Name": "New Releases" },
      { "id": "list24", "Name": "Bestselling Songs" },
      ],
      erikTag: [{ "id": "list31", "Name": "Artwork" },
      { "id": "list32", "Name": "Abstract" },
      { "id": "list33", "Name": "Acrylic Mediums" },
      { "id": "list34", "Name": "Creative Acrylic" },
      { "id": "list35", "Name": "Canvas Art" }
      ],
      graceTag: [{ "id": "list41", "Name": "Rock" },
      { "id": "list42", "Name": "Gospel" },
      { "id": "list43", "Name": "Latin Music" },
      { "id": "list44", "Name": "Jazz" },
      ],
      jacobTag: [{ "id": "list51", "Name": "100 Albums - $5 Each" },
      { "id": "list52", "Name": "Hip-Hop and R&B Sale" },
      { "id": "list53", "Name": "CD Deals" }
      ],
      visible: false,
      datasource: {},
      animation: { effect: 'None' },
      position: {},
    };
  },
  created() {
    this.datasource = { "Brooke": this.brookeTag, "Claire": this.claireTag, "Erik": this.erikTag, "Grace": this.graceTag, "Jacob": this.jacobTag };
  },
  mounted() {
    for (let i = 0; i < this.data.length; i++) {
      document.getElementById(this.data[i].Id).addEventListener("click", (e) => {
        this.renderDialog(e.currentTarget.id);
      });
    }
  },
  methods: {
    renderDialog: function (id) {
      if (document.getElementsByClassName('e-popup-open').length !== 0) {
        this.$refs.dialogInstance.hide();
      }
      else {
        this.listObj = document.getElementById('dialog').querySelector("#list").ej2_instances[0];
        this.listObj.dataSource = this.datasource[id];
        this.listObj.dataBind();
        this.$refs.dialogInstance.ej2Instances.position = { X: document.querySelector('.e-add-icon').getBoundingClientRect().left + 50, Y: document.querySelector('.e-add-icon').getBoundingClientRect().top - 5 };
        this.$refs.dialogInstance.show();
      }
    },
    addTag: function (e) {
      let listTag = document.createElement('span');
      listTag.className = 'advanced-option';
      let labelElem = document.createElement('span');
      labelElem.className = 'label';
      let deleteElem = document.createElement('span');
      deleteElem.className = 'delete';
      deleteElem.onclick = this.removeTag;
      labelElem.innerHTML = e.text;
      listTag.appendChild(labelElem);
      listTag.appendChild(deleteElem);
      let tag = document.createElement('span');
      tag.className = 'advanced-option-list';
      tag.appendChild(listTag);
      this.$refs.listviewInstance.$el.ej2_instances[0].element.querySelector('.e-active').appendChild(tag);
    },
    removeTag: function () {
      this.$refs.listviewInstance.$el.ej2_instances[0].selectedLI.children[1].remove();
    }
  }
}
</script>
<style>
#sample {
  padding: 40px;
}

.advanced-option-list {
  display: inline-flex;
  list-style: none;
  margin: 0;
  padding: 0;
  margin-top: 4px;
  overflow: auto;
}

.advanced-option {
  border: 1px solid #0078d7;
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
  padding: 0 20px 0 5px;
  margin: 0 3px 1px 0;
  float: left;
  position: relative;
  background: white;
  line-height: 21px;
}

.advanced-option .label {
  color: #0078d7;
  font-size: 10px;
}

.advanced-option .delete {
  background: url(https://research.isg-one.com/Assets/img/Content/icn/tiny-del.png) right center no-repeat;
  padding-left: 10px;
  padding-top: 2px;
  right: 0;
  position: absolute;
  top: 1px;
  cursor: pointer;
  height: 16px;
  width: 30px;
}

.designationstyle {
  float: right;
  position: relative;
  right: 10px;
}

.cont-bg {
  font-size: 17px;
  height: 46px;
  padding-top: 10px;
  width: 100%;
}

.designationstyle .e-add-icon::before {
  content: '\e823';
}

#sample .e-list-item {
  height: auto;
}

.e-dialog .e-dlg-header-content+.e-dlg-content {
  margin-top: -18px;
}
</style>