How To

Add and remove list items from the ListView component

You can add or remove list items from the ListView component using the addItem and removeItem methods. Refer to the following steps to add or remove a list item.

  • Render the ListView with data source, and use the template property to append the delete icon for each list item. Also, bind the click event for the delete icon using the actionComplete handler.

  • Render the Add Item button, and bind the click event. On the click event handler, pass data with random id to the addItem method to add a new list item on clicking the Add Item button.

  • Bind the click handler to the delete icon created in step 1. Within the click event, remove the list item by passing the delete icon list item to removeItem method.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'flat-list'>
    <!-- ListView element -->
    <ejs-listview id='sample-list-flat' ref='list' :dataSource='data' :fields='fields' :template='datatemplate' :actionComplete='onComplete' >
    </ejs-listview>
    </div>
   <ejs-button id="btn" v-on:click.native="onClick">Add Item</ejs-button>
  </div>
</template>
<style>
 #sample-list-flat {
  margin: 40px auto;
  max-width: 500px;
}

#btn {
  margin:40px auto;
  display:block;
}
/* csslint ignore:start */

@font-face {
  font-family: "e-icon";
  src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAAKAIAAAwAgT1MvMj1tSfIAAAEoAAAAVmNtYXDnEOdVAAABiAAAADZnbHlmXOniGAAAAcgAAAFAaGVhZBC1AhkAAADQAAAANmhoZWEIUQQDAAAArAAAACRobXR4CAAAAAAAAYAAAAAIbG9jYQCgAAAAAAHAAAAABm1heHABDgCYAAABCAAAACBuYW1lv4Bt4QAAAwgAAAIZcG9zdJx8QW4AAAUkAAAAOwABAAAEAAAAAFwEAAAAAAAD9AABAAAAAAAAAAAAAAAAAAAAAgABAAAAAQAApWcDV18PPPUACwQAAAAAANbRXpQAAAAA1tFelAAAAAAD9AP0AAAACAACAAAAAAAAAAEAAAACAIwAAgAAAAAAAgAAAAoACgAAAP8AAAAAAAAAAQQAAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5wDnAAQAAAAAXAQAAAAAAAABAAAAAAAABAAAAAQAAAAAAAACAAAAAwAAABQAAwABAAAAFAAEACIAAAAEAAQAAQAA5wD//wAA5wD//wAAAAEABAAAAAEAAAAAAAAAoAAAAAIAAAAAA/QD9AALAIsAAAEHFwcnByc3JzcXNwUfHz8fLx8PHgLuhIRrg4NrhIRrg4P9iQECAwQGBwcJCwsMDQ4PDxEREhMUFBUWFhcXFxkYGRkaGhkZGBkXFxcWFhUUFBMSEREPDw4NDAsLCQcHBgQDAgEBAgMEBgcHCQsLDA0ODw8RERITFBQVFhYXFxcZGBkZGhoZGRgZFxcXFhYVFBQTEhERDw8ODQwLCwkHBwYEAwICg4OGa4SEa4ODaoCE7hoZGRgZFxcXFhYVFBQTEhERDw8ODQwLCwkHBwYEAwIBAQIDBAYHBwkLCwwNDg8PERESExQUFRYWFxcXGRgZGRoaGRkYGRcXFxYWFRQUExIREQ8PDg0MCwsJBwcGBAMCAQECAwQGBwcJCwsMDQ4PDxEREhMUFBUWFhcXFxkYGRkAAAASAN4AAQAAAAAAAAABAAAAAQAAAAAAAQAGAAEAAQAAAAAAAgAHAAcAAQAAAAAAAwAGAA4AAQAAAAAABAAGABQAAQAAAAAABQALABoAAQAAAAAABgAGACUAAQAAAAAACgAsACsAAQAAAAAACwASAFcAAwABBAkAAAACAGkAAwABBAkAAQAMAGsAAwABBAkAAgAOAHcAAwABBAkAAwAMAIUAAwABBAkABAAMAJEAAwABBAkABQAWAJ0AAwABBAkABgAMALMAAwABBAkACgBYAL8AAwABBAkACwAkARcgZGVsZXRlUmVndWxhcmRlbGV0ZWRlbGV0ZVZlcnNpb24gMS4wZGVsZXRlRm9udCBnZW5lcmF0ZWQgdXNpbmcgU3luY2Z1c2lvbiBNZXRybyBTdHVkaW93d3cuc3luY2Z1c2lvbi5jb20AIABkAGUAbABlAHQAZQBSAGUAZwB1AGwAYQByAGQAZQBsAGUAdABlAGQAZQBsAGUAdABlAFYAZQByAHMAaQBvAG4AIAAxAC4AMABkAGUAbABlAHQAZQBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIAB1AHMAaQBuAGcAIABTAHkAbgBjAGYAdQBzAGkAbwBuACAATQBlAHQAcgBvACAAUwB0AHUAZABpAG8AdwB3AHcALgBzAHkAbgBjAGYAdQBzAGkAbwBuAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQIBAwARY2lyY2xlLWNsb3NlLS0tMDIAAAA=)
    format("truetype");
  font-weight: normal;
  font-style: normal;
}

/* csslint ignore:end */

#sample-list-flat .delete-icon::after {
  font-family: "e-icon";
  content: "\e700";
  float: right;
  cursor: pointer;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { ButtonPlugin } from '@syncfusion/ej2-vue-buttons';
Vue.use(ListViewPlugin);
Vue.use(ButtonPlugin);
var demoVue = Vue.component("demo", {
  template: `<div class='text-content'> {{data.text}} <span class = 'delete-icon'></span> </div>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
    return {
        data: [
          { text: "Hennessey Venom", id: "1", icon: "delete-icon" },
          { text: "Bugatti Chiron", id: "2", icon: "delete-icon" },
          { text: "Bugatti Veyron Super Sport", id: "3", icon: "delete-icon" },
          { text: "Aston Martin One- 77", id: "4", icon: "delete-icon" },
          { text: "Jaguar XJ220", id: "list-5", icon: "delete-icon" },
          { text: "McLaren P1", id: "6", icon: "delete-icon" }
        ],
        fields: { text: 'text', iconCSS: 'icon' },
        datatemplate: function () {
                return { template : demoVue}
            }
    };
  },
   methods: {
        deleteItem: function(args){
            args.stopPropagation();
            let liItem = args.target.parentElement.parentElement;
            this.$refs.list.removeItem(liItem);
            this.onComplete();
        },
        onComplete: function(args) {
            let iconEle = document.getElementsByClassName("delete-icon");
            var _this =this;
            //Event handler to bind the click event for delete icon
            Array.from(iconEle).forEach(function(element) {
                element.addEventListener("click", _this.deleteItem.bind(_this));
            });
        },
        onClick: function(){
            let data = {
                    text: "Koenigsegg - " + (Math.random() * 1000).toFixed(0),
                    id: (Math.random() * 1000).toFixed(0).toString(),
                    icon: "delete-icon"
                };
            this.$refs.list.addItem([data]);
        }
    }
}
</script>

Get selected items in the ListView component

Single or many items can be selected by users in the ListView component. An API is used to get selected items from the list items. This is called as the getSelectedItems method.

getSelectedItems method

This is used to get the details of the currently selected item from the list items. It returns the SelectedItem | SelectedCollection

The getSelectedItems method returns the following items from the selected list items.

Return type Purpose
text Returns the text of selected item lists
data Returns the whole data of selected list items, i.e., returns the fields data of selected li.
item Returns the collections of list items
Source
Preview
app.vue
<template>
  <div class="control-section">
    <ejs-listview ref='listview' id='sample-list' :dataSource='data' showCheckBox=true :fields='fields'></ejs-listview>
    <br/>
    <ejs-button id="btn"  v-on:click.native="onClick" >Get Selected Items</ejs-button>
    <div ref='valEle' id="val"> </div>
  </div>
</template>
<style>
#sample-list {
  display: block;
  max-width: 400px;
  margin: auto;
  border: 1px solid #dddddd;
  border-radius: 3px;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { ButtonPlugin } from '@syncfusion/ej2-vue-buttons';
Vue.use(ListViewPlugin);
Vue.use(ButtonPlugin);
var demoVue = Vue.component("demo", {
  template: `<div class='text-content'> {{data.text}} <span class = 'delete-icon'></span> </div>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
    return {
      data: [
    { text: 'Hennessey Venom', id: 'list-01' },
    { text: 'Bugatti Chiron', id: 'list-02', isChecked: true },
    { text: 'Bugatti Veyron Super Sport', id: 'list-03'},
    { text: 'SSC Ultimate Aero', id: 'list-04', isChecked: true  },
    { text: 'Koenigsegg CCR', id: 'list-05' },
    { text: 'McLaren F1', id: 'list-06' },
    { text: 'Aston Martin One- 77', id: 'list-07', isChecked: true },
    { text: 'Jaguar XJ220', id: 'list-08' }],
    fields: { id: 'id', isChecked:'isChecked' },
    };
  },
  methods: {
    onClick: function(event){
      let selecteditem =this.$refs.listview.getSelectedItems();
      this.$refs.valEle.innerHTML="";
      for(let i=0; i< selecteditem["data"].length; i++) {
      let listData = document.createElement('p');
      listData.innerHTML = "text : "+ selecteditem["text"][i]+" , "+"id : "+selecteditem["data"][i].id;
      this.$refs.valEle.append(listData);
      }
    }
  }
}
</script>

Use dynamic templates of ListView based on device

The Syncfusion Essential JS2 components are desktop and mobile-friendly. So, you can use Syncfusion components in both modes. The component templates are not always fixed. Applications may need to load various templates depending upon the device.

Integration

In the ListView component, template support is being used. In some cases, the component wrapper is always responsive across all devices, but the template contents are dynamically changed with unspecified (sample side) dimensions. CSS customization is also needed in sample-side to align template content responsively in both mobile and desktop modes. Here, two templates have been loaded for mobile and desktop modes. To check the device mode, a browser module has been imported from the ej2-base package.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'flat-list'>
    <!-- ListView element -->
    <ejs-listview id='List' ref='list' :dataSource='data' :fields='fields' :template="templatecheck ? mobtemplate : wintemplate" headerTitle='Syncfusion Blog' showHeader='true'>
    </ejs-listview>
    </div>
  </div>
</template>
<style>
@import './template.css';
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { Browser } from '@syncfusion/ej2-base';
Vue.use(ListViewPlugin);

var demoVue = Vue.component("demo", {
  template: `<div class="settings">
        <div id="postContainer">
            <div id="postImg">
                <img :src='data.image' /></div>
            <div id="content">
                <div id="info">
                    <div id="logo">
                        <div id="share">
                            <span class="share"></span> </div>
                        <div id="comments"> <span class="comments"></span> </div>
                        <div id="bookmark"> <span class="bookmark"></span> </div>
                    </div>
                </div>
                <div class="name">{{data.Name}}</div>
                <div class="description">{{data.content}}</div>
                <div class="timeStamp">{{data.timeStamp}} </div>
            </div>
        </div>
    </div>`,
  data() {
    return {
      data: {}
    };
  }
});
var tempVue = Vue.component("demo", {
  template: `<div class="settings">
        <div id="postContainer">
            <div id="postImg">
                <img :src='data.image' /></div>
            <div id="content">
                <div class="name">{{data.Name}}</div>
                <div class="description">{{data.content}}</div>
                <div id="info">
                    <div id="logo">
                        <div id="share">
                            <span class="share"></span> </div>
                        <div id="comments"> <span class="comments"></span> </div>
                        <div id="bookmark"> <span class="bookmark"></span> </div>
                    </div>
                    <div class="timeStamp">{{data.timeStamp}} </div>
                </div>
            </div>
        </div>
    </div>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
    return {
    data: [
        { Name: 'IBM Open-Sources Web Sphere Liberty Code', content: 'In September, IBM announced that it would be open-sourcing the code for WebSphere...', id: '1', image: 'https://ej2.syncfusion.com/demos/src/listview/images/1.png', timeStamp: 'Syncfusion Blog - October 19, 2017' },
        { Name: 'Must Reads: 5 Big Data E-books to upend your development', content: 'Our first e-book was published in May 2012-jQuery Succinctly was the start of over...', id: '2', image: 'https://ej2.syncfusion.com/demos/src/listview/images/2.png', timeStamp: 'Syncfusion Blog - October 18, 2017'  },
        { Name: 'The Syncfusion Global License: Your Questions, Answered ', content: 'Syncfusion recently hosted a webinar to cover the ins and outs of the Syncfusion global...', id: '4', image: 'https://ej2.syncfusion.com/demos/src/listview/images/3.png', timeStamp: 'Syncfusion Blog - October 18, 2017'  },
        { Name: 'Know: What is Coming from Microsoft this Fall ', content: 'On October 17, Microsoft will release its Fall Creators Update for the Windows 10 platform...', id: '5', image: 'https://ej2.syncfusion.com/demos/src/listview/images/6.png', timeStamp: 'Syncfusion Blog - October 17, 2017'  }],
    fields: { text:'Name' },
    mobtemplate: function () {
      return { template : demoVue}
    },
    wintemplate: function () {
      return { template : tempVue}
    },
    templatecheck: Browser.isDevice,
    };
  },
}
</script>

Load list items in child list dynamically

To load list items in child list dynamically, push the new list item data into the existing dataSource using the select event.

Refer to the following steps to load list item into the child list:

  1. Initially, render the ListView with the required data source.

  2. Bind the select event that triggers selecting list item in the ListView component. By using the select event, you can push the new list item to the child list of the data source on specifying its item index. Item index can be obtained from the SelectEventArgs of the select event.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'flat-list'>
    <!-- ListView element -->
    <ejs-listview id='listview' ref='list' :dataSource='data' :fields='fields' headerTitle='Folders' showHeader='true' showIcon='true' :select='onSelect'>
    </ejs-listview>
    </div>
  </div>
</template>
<style>
#listview {
    display: block;
    max-width: 400px;
    margin: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}


#listview.e-listview .e-list-icon {
    height: 24px;
    width: 30px;
}

.folder, .file  {
    background: url('https://ej2.syncfusion.com/demos/src/listview/images/file_icons.png') no-repeat;
    background-size: 300%;
}

.folder{
    background-position: -5px -460px;
}

.file {
    background-position: -5px -151px;
}

.list {
    color:deeppink !important;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
Vue.use(ListViewPlugin);

export default {
  data: function() {
    return {
      data:[
    {
        id: '01', text: 'Music', icon: 'folder',
        child: [
            { id: '01-01', text: 'Gouttes.mp3', icon: 'file' }
        ]
    },
    {
        id: '02', text: 'Videos', icon: 'folder',
        child: [
            { id: '02-01', text: 'Naturals.mp4', icon: 'file' },
            { id: '02-02', text: 'Wild.mpeg', icon: 'file' },
        ]
    },
    {
        id: '03', text: 'Documents', icon: 'folder',
        child: [
            { id: '03-01', text: 'Environment Pollution.docx', icon: 'file' },
            { id: '03-02', text: 'Global Water, Sanitation, & Hygiene.docx', icon: 'file' },
            { id: '03-03', text: 'Global Warming.ppt', icon: 'file' },
            { id: '03-04', text: 'Social Network.pdf', icon: 'file' },
            { id: '03-05', text: 'Youth Empowerment.pdf', icon: 'file' },
        ]
    },
    {
        id: '04', text: 'Pictures', icon: 'folder',
        child: [
            {
                id: '04-01', text: 'Camera Roll', icon: 'folder',
                child: [
                    { id: '04-01-01', text: 'WIN_20160726_094117.JPG', icon: 'file' },
                    { id: '04-01-02', text: 'WIN_20160726_094118.JPG', icon: 'file' },
                    { id: '04-01-03', text: 'WIN_20160726_094119.JPG', icon: 'file' }
                ]
            },
            {
                id: '04-02', text: 'Wind.jpg', icon: 'file'
            },
            {
                id: '04-02', text: 'Stone.jpg', icon: 'file'
            },
            {
                id: '04-02', text: 'Home.jpg', icon: 'file'
            },
            {
                id: '04-02', text: 'Bridge.png', icon: 'file'
            }
        ]
    },
    {
        id: '05', text: 'Downloads', icon: 'folder',
        child: [
            { id: '05-01', text: 'UI-Guide.pdf', icon: 'file' },
            { id: '05-02', text: 'Tutorials.zip', icon: 'file' },
            { id: '05-03', text: 'Game.exe', icon: 'file' },
            { id: '05-04', text: 'TypeScript.7z', icon: 'file' },
        ]
    },
],
    fields: { iconCss: 'icon', tooltip: 'text'},
    };
  },
  methods:{
    onSelect: function(args){
      this.$refs.list.dataSource[args.index].child.push({ id: '01-02', text: 'Newly Added File', icon: 'file', htmlAttributes: { role: 'li', class: 'list' } });
    }
  }
}
</script>

List Items Count in Group Header

The ListView component supports wrapping list items into a group based on the category. The category of each list item can be mapped with groupBy field of the data source. You can display grouped list items count in the list-header using the group header template. Refer to the following code sample to display grouped list item count.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'flat-list'>
    <!-- ListView element -->
    <ejs-listview id='List' ref='list' :dataSource='data' :fields='fields' :template="demotemplate" :groupTemplate="grouptemplate" >
    </ejs-listview>
    </div>
  </div>
</template>
<style>
  .count{
    float:right;
  }
  #List {
      display: block;
      margin: auto;
      border: 1px solid;
      border-color: #ccc;
      border-color: rgba(0, 0, 0, 0.12);
      width: 60%;
  }

  #List .settings {
      height: auto;
  }

  #List .e-list-item {
      height: auto;
      padding: 0;
      cursor: pointer;
      box-sizing: border-box;
      border: 0.1px solid #ccc;
  }
  #List .e-list-header .e-text {
      font-family: sans-serif;
      font-size: 18px;
      line-height: 26px;
  }
  #List #content {
    margin: 3px;
  }
  #List .name {
      font-size: 12px;
      line-height: 25px;
  }

  #info {
    line-height: 20px;
    font-size: 12px;
  }

  #postImg {
     margin: 9px;
 }

  #postContainer {
  width: inherit;
  margin: auto;
  display: inline-flex;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
Vue.use(ListViewPlugin);
var demoVue = Vue.component("demo", {
  template: `<div class="settings">
              <div id="postContainer">
                  <div id="postImg">
                      <img :src='data.image' style="height:35px;width:35px;border-radius:50%; border: 1px solid #ccc;" /></div>
                  <div id="content">
                      <div class="name">{{data.Name}}</div>
                      <div id="info">{{data.contact}}</div>
                  </div>
              </div>
          </div>`,
  data() {
    return {
      data: {}
    };
  }
});
var tempVue = Vue.component("demo", {
  template: `<div><span class="category">{{data.items[0].category}}</span> <span class="count"> {{data.items.length}} Item(s)</span></div>`,
  data() {
    return {
      data: {}
    };
  }
});
export default {
  data: function() {
    return {
      data: [
        { Name: 'Nancy', contact:'(206) 555-985774', id: '1', image: 'https://ej2.syncfusion.com/demos/src/grid/images/1.png',  category: 'Experience'},
        { Name: 'Janet', contact: '(206) 555-3412', id: '2', image: 'https://ej2.syncfusion.com/demos/src/grid/images/3.png', category: 'Fresher' },
        { Name: 'Margaret', contact:'(206) 555-8122', id:'4', image: 'https://ej2.syncfusion.com/demos/src/grid/images/4.png', category: 'Experience' },
        { Name: 'Andrew ', contact:'(206) 555-9482', id: '5', image: 'https://ej2.syncfusion.com/demos/src/grid/images/2.png', category: 'Experience'},
        { Name: 'Steven', contact:'(71) 555-4848', id: '6', image: 'https://ej2.syncfusion.com/demos/src/grid/images/5.png', category: 'Fresher' },
        { Name: 'Michael', contact:'(71) 555-7773', id: '7', image: 'https://ej2.syncfusion.com/demos/src/grid/images/6.png', category: 'Experience' },
        { Name: 'Robert', contact:'(71) 555-5598', id: '8', image: 'https://ej2.syncfusion.com/demos/src/grid/images/7.png', category: 'Fresher' },
        { Name: 'Laura', contact:'(206) 555-1189', id: '9', image: 'https://ej2.syncfusion.com/demos/src/grid/images/8.png', category: 'Experience' },
    ],
         fields: {text: 'Name', groupBy: 'category'},
       demotemplate: function () {
                return { template : demoVue}
            },
             grouptemplate: function () {
                return { template : tempVue}
            },
    };
  },
}
</script>

Customize ListView as mobile contact layout

You can customize the ListView using the template property. Refer to the following steps to customize ListView as mobile contact view with our ej2-avatar.

  • Render the ListView with dataSource that has avatar data. You can set avatar data as either text or class names. Refer to the following codes.
dataSource: [
  {
    text: "Jenifer", contact: "(206) 555-985774", id: "1", avatar: "", pic: "pic01"
  },
  {
    text: "Amenda", contact: "(206) 555-3412", id: "2", avatar: "A", pic: ""
  }
];
  • Set avatar classes in ListView template to customize contact icon. In the following codes, medium size avatar has been set using the class name e-avatar e-avatar-circle from data source.
  <div class="listWrapper">
        <span :class="['e-avatar e-avatar-small e-avatar-circle']" v-if="data.avatar !== ''">{{data.avatar}}</span>
        <span :class="[data.pic + ' e-avatar e-avatar-small e-avatar-circle']" v-if="data.pic !== '' "> </span>
        <span class="list-text">{{data.text}}</span>
    </div>

Avatars can be set in different sizes in avatar classes. To know more about avatar classes, refer to Avatar.

Source
Preview
app.vue
<template>
    <div id="sample">
       <ejs-listview id='List' :dataSource='data' :fields='fields' :template='template' width="350px" showHeader='true' headerTitle='Contacts' sortOrder='Ascending'></ejs-listview>
    </div>
</template>
<style>
#List {
  margin: 0 auto;
  border: 1px solid #ccc;
}

#List .e-list-item {
  height: 60px;
  cursor: pointer;
}

#List .e-list-header .e-text {
  font-family: sans-serif;
  font-size: 18px;
  line-height: 16px;
}

#List #content {
  margin: 0;
}

#List .e-list-header{
  background: rgb(2, 120, 215);
  color: white;
}

#List #info,
#List .name {
  font-size: 14px;
  margin: 0 60px;
  line-height: 20px;
}

#List .name {
  padding-top: 8px;
  font-weight: 500;
}

.pic01 {
  background-image: url("https://ej2.syncfusion.com/demos/src/grid/images/1.png");
}

.pic02 {
  background-image: url("https://ej2.syncfusion.com/demos/src/grid/images/3.png");
}

.pic03 {
  background-image: url("https://ej2.syncfusion.com/demos/src/grid/images/5.png");
}

.pic04 {
  background-image: url("https://ej2.syncfusion.com/demos/src/grid/images/2.png");
}

#List .e-avatar {
  position: absolute;
  margin-top: 8px;
  font-size: 14px;
}

#List .e-list-item:nth-child(1) .e-avatar {
  background-color: #039be5;
}

#List .e-list-item:nth-child(2) .e-avatar {
  background-color: #e91e63;
}

#List .e-list-item:nth-child(6) .e-avatar {
  background-color: #009688;
}

#List .e-list-item:nth-child(8) .e-avatar {
  background-color: #0088;
}

</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
Vue.use(ListViewPlugin);
var demoVue = Vue.component("demo", {
  template: `
    <div class="settings">
        <span :class="['e-avatar e-avatar-circle']" v-if="data.avatar !== ''">{{data.avatar}}</span>
        <span :class="[data.pic + ' e-avatar e-avatar-circle']" v-if="data.pic !== '' "> </span>
        <div id="content">
        <div class="name">{{data.text}}</div>
        <div id="info">{{data.contact}}</div>
        </div>
    </div>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
    return {
      data: [
  {
    text: "Jenifer",
    contact: "(206) 555-985774",
    id: "1",
    avatar: "",
    pic: "pic01"
  },
  { text: "Amenda", contact: "(206) 555-3412", id: "2", avatar: "A", pic: "" },
  {
    text: "Isabella",
    contact: "(206) 555-8122",
    id: "4",
    avatar: "",
    pic: "pic02"
  },
  {
    text: "William ",
    contact: "(206) 555-9482",
    id: "5",
    avatar: "W",
    pic: ""
  },
  {
    text: "Jacob",
    contact: "(71) 555-4848",
    id: "6",
    avatar: "",
    pic: "pic04"
  },
  { text: "Matthew", contact: "(71) 555-7773", id: "7", avatar: "M", pic: "" },
  {
    text: "Oliver",
    contact: "(71) 555-5598",
    id: "8",
    avatar: "",
    pic: "pic03"
  },
  {
    text: "Charlotte",
    contact: "(206) 555-1189",
    id: "9",
    avatar: "C",
    pic: ""
  }
],
    fields: {text: 'text'},
    template: function () {
                return { template : demoVue};
            }
    };
  },
}
</script>

Filter list items in the ListView component

The filtered data can be displayed in the ListView component depending upon on user inputs using the DataManager. Refer to the following steps to render the ListView with filtered data.

  • Render a textbox to get input for filtering data.

  • Render ListView with dataSource, and set the sortOrder property.

  • Bind the keyup event for textbox to perform filtering operation. To filter list data, pass the list data source to the DataManager, manipulate the data using the executeLocal method, and then update filtered data as ListView dataSource.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'sample'>
      <input class="e-input" type="text" id='textbox' ref='textboxEle' placeholder="Filter" title="Type in a name" @keyup='onkeyup' />
      <!-- ListView element -->
      <ejs-listview id='list' ref='listObj' :dataSource='data' :fields='fields' sortOrder='Ascending'>
      </ejs-listview>
    </div>
  </div>
</template>
<style>
#list {
  box-shadow: 0 1px 4px #ddd;
  border-bottom: 1px solid #ddd;
}
#sample {
  height: 220px;
  margin: 0 auto;
  display: table;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { DataManager, Query, ODataV4Adaptor } from "@syncfusion/ej2-data";
Vue.use(ListViewPlugin);

export default {
  data: function() {
    return {
      data: [
        { text: "Hennessey Venom", id: "list-01" },
        { text: "Bugatti Chiron", id: "list-02" },
        { text: "Bugatti Veyron Super Sport", id: "list-03" },
        { text: "SSC Ultimate Aero", id: "list-04" },
        { text: "Koenigsegg CCR", id: "list-05" },
        { text: "McLaren F1", id: "list-06" }],
      fields: { text: "text", id: "id" },
    };
  },
  methods:{
   onkeyup: function(event){
      let keyupvalue = this.$refs.textboxEle.value;
      let data = new DataManager(this.data).executeLocal(new Query().where("text", "startswith", keyupvalue, true));
      if (!keyupvalue) {
        this.$refs.listObj.dataSource = this.data.slice();
      } else {
        this.$refs.listObj.dataSource = data;
      }
    }
  }
}
</script>

In this demo, data has been filtered with starting character of the list items. You can also filter list items with ending character by passing the endswith in where clause instead of startswith.

Hide checkbox in listview

The checkbox of the any list item can be hidden by using htmlAttributes of fields object. With the help of htmlAttributes we can add unique class to each list item that will be rendered from the data source, from the CSS class we can hide the checkbox of the list item.

In this sample, we had hidden the multiple leaf node of nested list. The e-checkbox-hidden class has been added in the data source where the checkbox needs to be hidden. Refer the below snippet for simple data source.

    {
        'text': 'New York',
        'id': '3002',
        'category': 'USA',
        'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' }
    }

Even though we have hidden the checkbox the functionality will be same for the list item which might affect the getSelectedItems method. So, to counteract that we will follow certain logic in the select event. The Logic here is to remove the e-active class from the other checkbox hidden list item which will be added when we select on that item and retain e-active on currently selected item.

In this process we will exclude the visible checkbox list items and only consider the hidden checkbox items.

Source
Preview
app.vue
<template>
  <div class="control-section">
    <div id = 'flat-list'>
      <!-- ListView element -->
       <ejs-listview id='folderCheckbox' ref='listviewInstance' :dataSource='data' :fields='fields' sortOrder='Ascending' showHeader=true headerTitle='Mixed Leaf Checkbox Hidden List' showCheckBox=true :select='onSelect'>
       </ejs-listview>
    </div>
  </div>
</template>
<style>
  #folderCheckbox {
    border: 1px solid #dddddd;
    border-radius: 3px;
    margin: 5px;
    width: 400px;
  }
  #folderCheckbox .e-checkbox-hidden .e-checkbox-wrapper {
    visibility: hidden;
  }
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
Vue.use(ListViewPlugin);

export default {
  data: function() {
    return {
      data: [
    {
        'text': 'Asia',
        'id': '01',
        'category': 'Continent',

        'child': [{
            'text': 'India',
            'id': '1',
            'category': 'Asia',

            'child': [{
                'text': 'Delhi',
                'id': '1001',
                'category': 'India',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Kashmir',
                'id': '1002',
                'category': 'India',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Goa',
                'id': '1003',
                'category': 'India',
                'htmlAttributes': { 'class': 'e-file' },
            },
            ]
        },
        {
            'text': 'China',
            'id': '2',
            'category': 'Asia',

            'child': [{
                'text': 'Zhejiang',
                'id': '2001',
                'category': 'China',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Hunan',
                'id': '2002',
                'category': 'China',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Shandong',
                'id': '2003',
                'category': 'China',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        }]
    },

    {
        'text': 'North America',
        'id': '02',
        'category': 'Continent',

        'child': [{
            'text': 'USA',
            'id': '3',
            'category': 'North America',

            'child': [{
                'text': 'California',
                'id': '3001',
                'category': 'USA',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'New York',
                'id': '3002',
                'category': 'USA',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Florida',
                'id': '3003',
                'category': 'USA',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        },
        {
            'text': 'Canada',
            'id': '4',
            'category': 'North America',

            'child': [{
                'text': 'Ontario',
                'id': '4001',
                'category': 'Canada',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Alberta',
                'id': '4002',
                'category': 'Canada',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Manitoba',
                'id': '4003',
                'category': 'Canada',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        }]
    },

    {
        'text': 'Europe',
        'id': '03',
        'category': 'Continent',

        'child': [{
            'text': 'Germany',
            'id': '5',
            'category': 'Europe',

            'child': [{
                'text': 'Berlin',
                'id': '5001',
                'category': 'Germany',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Bavaria',
                'id': '5002',
                'category': 'Germany',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Hesse',
                'id': '5003',
                'category': 'Germany',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            }]
        }, {
            'text': 'France',
            'id': '6',
            'category': 'Europe',

            'child': [{
                'text': 'Paris',
                'id': '6001',
                'category': 'France',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Lyon',
                'id': '6002',
                'category': 'France',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Marseille',
                'id': '6003',
                'category': 'France',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        }]
    },
    {
        'text': 'Australia',
        'id': '04',
        'category': 'Continent',

        'child': [{
            'text': 'Australia',
            'id': '7',
            'category': 'Australia',

            'child': [{
                'text': 'Sydney',
                'id': '7001',
                'category': 'Australia',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Melbourne',
                'id': '7002',
                'category': 'Australia',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Brisbane',
                'id': '7003',
                'category': 'Australia',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        }, {
            'text': 'New Zealand',
            'id': '8',
            'category': 'Australia',

            'child': [{
                'text': 'Milford Sound',
                'id': '8001',
                'category': 'New Zealand',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Tongariro National Park',
                'id': '8002',
                'category': 'New Zealand',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Fiordland National Park',
                'id': '8003',
                'category': 'New Zealand',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            }]
        }]
    },
    {
        'text': 'Africa',
        'id': '05',
        'category': 'Continent',

        'child': [{
            'text': 'Morocco',
            'id': '9',
            'category': 'Africa',

            'child': [{
                'text': 'Rabat',
                'id': '9001',
                'category': 'Morocco',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Toubkal',
                'id': '9002',
                'category': 'Morocco',
                'htmlAttributes': { 'class': 'e-file' },
            },
            {
                'text': 'Todgha Gorge',
                'id': '9003',
                'category': 'Morocco',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            }]
        }, {
            'text': 'South Africa',
            'id': '10',
            'category': 'Africa',

            'child': [{
                'text': 'Cape Town',
                'id': '10001',
                'category': 'South Africa',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Pretoria',
                'id': '10002',
                'category': 'South Africa',
                'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' },
            },
            {
                'text': 'Bloemfontein',
                'id': '10003',
                'category': 'South Africa',
                'htmlAttributes': { 'class': 'e-file' },
            }]
        }]
    }
],
    fields: { tooltip: "text" },
    };
  },
  methods:{
   onSelect: function(args){
      let normalElements = Array.prototype.slice.call(this.$refs.listviewInstance.$el.ej2_instances[0].curUL.getElementsByClassName('e-checkbox-hidden'));

        // Looping through all the selected element and removing e-active class
        // to avoid behaviour interference  with getSelectedItems method
        normalElements.forEach((element) => {
            element.classList.remove('e-active');
        });

        // Finally adding e-active class to currently selected item except checkbox item.
        // because if it is checkbox item their actions will taken care from the source side itself.
        if (args.item.classList.contains('e-checkbox-hidden')) {
            args.item.classList.add('e-active');
        }
    }
  }
}
</script>

Trace all events in ListView

The ListView component triggers events based on its actions. The events can be used as extension points to perform custom operations. Refer to the following steps to trace the ListView events:

  1. Render the ListView with dataSource, and bind the actionBegin, actionComplete, and select events.

  2. Perform custom operations in actionBegin, actionComplete, and select events.

  3. Provide event log details for actionBegin and actionComplete events, and they will be displayed in the event trace panel when the ListView action starts and the dataSource bound successfully.

  4. Get the selected item details from the SelectEventArgs in the select event, and display the selected list item text in the event trace panel while selecting list items.

  5. Use clear button to remove event trace information.

Source
Preview
app.vue
<template>
  <div id="sample">
    <div class="content-wrapper">
    <!-- ListView element -->
      <ejs-listview id='listview-def' ref='listviewInstance' :dataSource='data' width=250 :actionBegin='onBegin' :actionComplete='onComplete' :select='onSelect'>
      </ejs-listview>
    </div>
    <div id="list_event">
      <h4><b>Event Trace</b></h4>
      <div id="evt">
        <div class="eventarea" style="height:273px;overflow: auto">
          <span ref='EventLogEle' class="EventLog" id="EventLog" style="word-break: normal;"></span>
        </div>
        <div class="evtbtn">
          <ejs-button id="clear" @click.native='onClick'>Clear</ejs-button>
        </div>
      </div>
    </div>
  </div>
</template>
<style>
#EventLog b {
  color: #388e3c;
}

#listview-def {
  border: 1px solid #dcdcdc;
}
.content-wrapper {
  padding-left: 40px;
  padding-top: 36px;
}

.evtbtn {
  margin-top: 40px;
  margin-left: 70px;
}

/* csslint ignore:start */

hr {
  margin-top: 6px !important;
  margin-bottom: 6px !important;
}

/* csslint ignore:end */

#evt {
  border: 1px solid #dcdcdc;
  padding: 10px;
  min-width: 10px;
}

#sample {
  display: inline-flex;
}

.eventarea {
  min-width: 250px;
}

#list_event {
  margin-top: -25px;
  padding-left:40px;
  min-width: 200px;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import {ButtonPlugin } from "@syncfusion/ej2-vue-buttons";
Vue.use(ListViewPlugin);
Vue.use(ButtonPlugin);

export default {
  data: function() {
    return {
      data: [
  { text: "Hennessey Venom", id: "list-01" },
  { text: "Bugatti Chiron", id: "list-02" },
  { text: "Bugatti Veyron Super Sport", id: "list-03" },
  { text: "SSC Ultimate Aero", id: "list-04" },
  { text: "Koenigsegg CCR", id: "list-05" },
  { text: "McLaren F1", id: "list-06" },
  { text: "Aston Martin One- 77", id: "list-07" },
  { text: "Jaguar XJ220", id: "list-08" },
  { text: "McLaren P1", id: "list-09" },
  { text: "Ferrari LaFerrari", id: "list-10" }
]
    };
  },
  methods:{
    onBegin: function(){
     this.appendElement("<b>actionBegin </b> event is triggered<hr>");
    },
    onComplete: function(){
      this.appendElement("<b>actionComplete</b> is triggered <hr>");
    },
    onSelect: function(args){
    this.appendElement(args.text + "<b>&nbsp;&nbsp;is selected</b><hr>");
    },
    appendElement: function(html) {
     let span = document.createElement("span");
     span.innerHTML = html;
     let log = this.$refs.EventLogEle;
     log.insertBefore(span, log.firstChild);
    },
    onClick: function(event){
       this.$refs.EventLogEle.innerHTML = "";
    }
  }
}
</script>

Load the spinner until list items are loaded

The features of the ListView component such as remote data-binding take more time to fetch data from corresponding dataSource/remote URL. In this case, you can use EJ2 Spinner to enhance the appearance of the UI. This section explains how to load a spinner component to groom the appearance.

Refer to the following code sample to render the spinner component.

    createSpinner({
        target: this.spinnerEle.nativeElement
    });
    showSpinner(this.spinnerEle.nativeElement);

Here, the data is fetched from Northwind Service URL; it takes a few seconds to load the data. To enhance the UI, the spinner component has been rendered initially. After the data is loaded from remote URL, the spinner component will be hidden in ListView actionComplete event.

Source
Preview
app.vue
<template>
 <div class="control-section">
    <div id="flat-list">
    <!-- ListView element -->
    <ejs-listview id='sample-list' :dataSource='data' :query='query' :fields='fields' :headerTitle='headerTitle' showHeader='true' :actionComplete='onComplete'></ejs-listview>
    </div>
    <div ref='spinnerEle' id="spinner" ></div>
  </div>
</template>
<style>
#sample-list {
    border: 1px solid #dddddd;
    border-radius: 3px;
    margin: auto;
}
#flat-list {
    width: 50%;
    padding: 10px;
    margin: auto;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { DataManager, Query } from '@syncfusion/ej2-data';
import { createSpinner, showSpinner, setSpinner } from '@syncfusion/ej2-vue-popups';

Vue.use(ListViewPlugin);
export default {
  data: function() {
     return {
      data: new DataManager({
        url: '//js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/',
        crossDomain: true
      }),
      query: new Query().from('Products').select('ProductID,ProductName').take(10),
      fields:  { id: 'ProductID', text: 'ProductName' },
      headerTitle: 'Products',
    };
  },
  mounted: function(){
     createSpinner({ target: this.$refs.spinnerEle });
    showSpinner(this.$refs.spinnerEle);
  },
  methods: {
    onComplete: function(){
      this.$refs.spinnerEle.style.display = "none";
    }
  }
}
</script>

Customize dual list

The dual list contains two ListView. This allows you to move list items from one list to another using the client-side events. This section explains how to integrate the ListView component to achieve dual list.

Use cases

  • Stock exchanges of two different countries
  • Job applications (skill sets)

Integration of Dual List

Here, two ListView components have been used to display the list items. An ej2-button is used to transfer data between the ListView, and a textbox is used to achieve the UI of filtering support.

The dual list supports:

  • Moving whole data from one list to another.
  • Moving selected data from one list to another.
  • Filtering the list by using a client-side typed character.

In the ListView component, sorting is enabled using the sortOrder property, and the select event is triggered while selecting an item. Here, the select event is triggered to enable and disable button states.

Manipulating data

Moving whole data from the first list to the second list(>>)

  • Here, the whole data can be moved from the first ListView to the second by clicking the first button. When clicking the button, the whole list items are sliced, and concat with the second ListView. This button is enabled only when the data source of the first ListView is not empty.

Moving whole data from the second list to the first list(<<)

  • The functionality of the second button is the same as above, and data is transferred from the second list to the first list. This button is enabled only when the data source of the second ListView is not empty.

Moving selected item from one list to another list (>) and (<)

  • The Select event is triggered when selecting a list item in the ListView. The selected items can be transferred between two lists. These buttons will be enabled when selecting an item in lists.

Filtering method

  • The filtering method is used to filter list items when typing a character in the text box. In this method, the dataManager has been used to fetch data from the data source and display in ListView.

Sorting

  • By using the dual list, list items can be sorted in the ListView component using the sortOrder property. You can enable sorting in one ListView; in the same order, data can be transferred to another ListView.
Source
Preview
app.vue
<template>
 <div>
            <div id="text1">
            <input ref='textboxEle' class="e-input" type="text" id="firstInput" placeholder="Filter" title="Type in a name" @keyup="onFirstKeyUp" />
              </div>
            <ejs-listview ref='firstListObj' id='list-1' :dataSource='firstListData' :fields='fields' sortOrder='Ascending' :select="onFirstListSelect"></ejs-listview>
             <div id="btn">
             <ejs-button ref='firstBtnObj' id="firstBtn" @click.native="firstbtnclick"> >> </ejs-button>
             <ejs-button ref='secondBtnObj' id="secondBtn" :disabled='disabled' @click.native="secondbtnclick"> > </ejs-button>
             <ejs-button ref='thirdBtnObj' id="thirdBtn" :disabled='disabled' @click.native="thirdbtnclick"> < </ejs-button>
             <ejs-button ref='fourthBtnObj' id="fourthBtn" @click.native="fourthbtnclick"> << </ejs-button>
             </div>

            <div id="text2">
            <input ref='textEle' class="e-input" type="text" id="secondInput" placeholder="Filter" title="Type in a name" @keyup="onSecondKeyUp" />
            </div>
            <ejs-listview ref='secondListObj' id='list-2' :dataSource='secondListData' :fields='fields' sortOrder='Ascending' :select="onSecondListSelect"></ejs-listview>
</div>
</template>
<style>
#list-1,
    #list-2 {
        width: 40%;
        height: 430px;
        box-shadow: 0 1px 4px #ddd;
        border-bottom: 1px solid #ddd;
    }
    #firstList, #secondList {
        margin-top: 13px;
    }

    .e-btn {
        width: 60px;
        height: 60px;
        margin-bottom: 15px;
    }

    #btn {
        float: left;
        width: 5%;
        padding-left: 30px;
        margin-top: 67px;
    }

    #list-1 {
        float: left;
    }

    #list-2 {
        float: right;
    }
    #firstInput {
        width: 40%;
    }
    #secondInput {
      margin-left: 70px;
      width: 56%;
    }
    #text2 {
        margin-top: -23px;
        margin-left: 194px;
    }
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { ButtonPlugin } from "@syncfusion/ej2-vue-buttons";
import { enableRipple } from '@syncfusion/ej2-base';
import { DataManager, Query, ODataV4Adaptor } from "@syncfusion/ej2-data";
enableRipple(true);
Vue.use(ListViewPlugin);
Vue.use(ButtonPlugin);

Vue.use(ListViewPlugin);
export default {
  data: function() {
      return {
      firstListData: [
  { text: "Hennessey Venom", id: "list-01" },
  { text: "Bugatti Chiron", id: "list-02" },
  { text: "Bugatti Veyron Super Sport", id: "list-03" },
  { text: "SSC Ultimate Aero", id: "list-04" },
  { text: "Koenigsegg CCR", id: "list-05" },
  { text: "McLaren F1", id: "list-06" }
],
secondListData : [
    { text: 'Aston Martin One- 77', id: 'list-07' },
    { text: 'Jaguar XJ220', id: 'list-08' },
    { text: 'McLaren P1', id: 'list-09' },
    { text: 'Ferrari LaFerrari', id: 'list-10' },
  ],
      fields:  {text: "text", id: "id" },
      disabled: true
    };
  },
  mounted: function(){
      this.firstListData = this.$refs.firstListObj.dataSource.slice();
      this.secondListData = this.$refs.secondListObj.dataSource.slice();
  },
  methods: {
    firstbtnclick: function() {
        this.$refs.secondListObj.dataSource = Array.prototype.concat.call(this.$refs.firstListObj.dataSource, this.$refs.secondListObj.dataSource);
        this.$refs.secondListObj.dataBind();
        this.updateFirstListData();
        this.$refs.firstListObj.removeMultipleItems(this.$refs.firstListObj.$el.ej2_instances[0].liCollection);
        this.firstListData = this.firstListData.concat(this.$refs.firstListObj.dataSource);
        this.secondListData = this.$refs.secondListObj.dataSource.slice();
        this.$refs.firstBtnObj.$el.disabled = true;
        this.onFirstKeyUp();
        this.setButtonState();
    },

  secondbtnclick:function() {   //Here, the selected list items are moved to the second list on clicking move button
        let e = this.$refs.firstListObj.getSelectedItems();
        this.$refs.secondListObj.dataSource = Array.prototype.concat.call(this.$refs.secondListObj.dataSource, e.data);
        this.$refs.firstListObj.removeItem(e.item);
        this.firstListData = this.$refs.firstListObj.dataSource;
        this.secondListData = this.$refs.secondListObj.dataSource.slice();
        this.onFirstKeyUp();
        this.$refs.secondBtnObj.$el.disabled = true;
        this.setButtonState();
    },

   thirdbtnclick: function() {  //Here, the selected list items are moved to the first list on clicking move button
        let e = this.$refs.secondListObj.getSelectedItems();
        this.$refs.firstListObj.dataSource = Array.prototype.concat.call(this.$refs.firstListObj.dataSource, e.data);
        this.$refs.secondListObj.removeItem(e.item);
        this.secondListData = this.$refs.secondListObj.dataSource;
        this.firstListData = this.$refs.firstListObj.dataSource.slice();
        this.onSecondKeyUp();
        this.$refs.thirdBtnObj.$el.disabled = true;
        this.setButtonState();

    },

   fourthbtnclick: function() {   //Here, all list items are moved to the first list on clicking move all button
        this.$refs.firstListObj.dataSource = Array.prototype.concat.call(this.$refs.firstListObj.dataSource, this.$refs.secondListObj.dataSource);
        this.updateSecondListData();
        this.$refs.secondListObj.removeMultipleItems(this.$refs.secondListObj.$el.ej2_instances[0].liCollection);
        this.secondListData = this.secondListData.concat(this.$refs.secondListObj.dataSource);
        this.firstListData = this.$refs.firstListObj.dataSource.slice();
        this.onSecondKeyUp();
        this.setButtonState();

    },

    updateFirstListData: function() {  //Here, the ListView data source is updated to the first list
        Array.prototype.forEach.call(this.$refs.firstListObj.$el.ej2_instances[0].liCollection, (list) => {
            this.firstListData.forEach((data, index) => {
                if (list.innerText.trim() === data.text) {
                  this.firstListData.splice(index, 1)
                }
            });
        });
        this.$refs.textboxEle.value= '';
        let ds = [];
        this.firstListData.forEach((data) => {
            ds.push(data);
        });
        this.firstListData = ds;

    },

    //Here, the ListView dataSource is updated for the second list
    updateSecondListData: function() {
        Array.prototype.forEach.call(this.$refs.secondListObj.$el.ej2_instances[0].liCollection, (list) => {
            this.secondListData.forEach((data, index) => {
                if (list.innerText.trim() === data.text){
                    this.secondListData.splice(index, 1);
                }
            });
        });
        this.$refs.textEle.value = '';
        let ds = [];
        this.secondListData.forEach((data) => {
            ds.push(data);
        });
        this.secondListData = ds;

    },
    onFirstListSelect: function() {
        this.$refs.secondBtnObj.disabled = false;
    },
    onSecondListSelect: function() {
        this.$refs.thirdBtnObj.disabled = false;
    },
    //Here, filtering is handled using the dataManager for the first list
    onFirstKeyUp: function(e) {
        let value = this.$refs.textboxEle.value;
        let data = new DataManager(this.firstListData).executeLocal(new Query().where('text', 'startswith', value, true));
        if (!value) {
            this.$refs.firstListObj.dataSource = this.firstListData.slice();
        } else {
            this.$refs.firstListObj.dataSource = data;
        }
    },
    //Here, filtering is handled using the dataManager for the second list
     onSecondKeyUp:function(e) {
        let value =this.$refs.textEle.value;
        let data = new DataManager(this.secondListData).executeLocal(new Query().where('text', 'startswith', value, true));
        if (!value) {
            this.$refs.secondListObj.dataSource = this.secondListData.slice();
        } else {
            this.$refs.secondListObj.dataSource = data;
        }
    },
    //Here, the state of the button is changed
    setButtonState: function() {
        if (this.$refs.firstListObj.dataSource.length) {
            this.$refs.firstBtnObj.$el.disabled = false;
        } else {
            this.$refs.firstBtnObj.$el.disabled = true;
            this.$refs.secondBtnObj.$el.disabled = true;
        }

        if (this.$refs.secondListObj.dataSource.length) {
            this.$refs.fourthBtnObj.$el.disabled = false;
        } else {
            this.$refs.fourthBtnObj.$el.disabled = true;
            this.$refs.thirdBtnObj.$el.disabled = true;
        }

    }
  }
}
</script>

Customize ListView with dynamic tags

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 dynamic ListView with required property that holds the tags of parent ListView, and bind the select event (triggers when the list item is selected), in which you can get and add the selected item value as tags into 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 on created event.

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

//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 with added dynamic tags to remove it. Refer to the following code sample.
//Method to remove the list item
removeTag() {
    this.$refs.listviewInstance.$el.ej2_instances[0].selectedLI.children[1].remove();
}
Source
Preview
app.vue
<template>
  <div id="sample">
    <ejs-listview ref='listviewInstance' id='templatelist' :dataSource='data' :fields='fields' width='350' :template='listTemplate'>
    </ejs-listview>
     <ejs-dialog id='dialog' ref='dialogInstance' width='200px' :content='dialogcontent' :animationSettings='animation' :visible='visible' showCloseIcon='true' :position='position' :created='createList'>
      </ejs-dialog>
  </div>
</template>
<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>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { ButtonPlugin } from "@syncfusion/ej2-vue-buttons";
import { DialogPlugin } from "@syncfusion/ej2-vue-popups";

Vue.use(ListViewPlugin);
Vue.use(ButtonPlugin);
Vue.use(DialogPlugin);

var listVue = Vue.component("demo", {
  template: ` <div><span class="templatetext">{{data.Name}} </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>`,
  data() {
    return {
      data: {}
    };
  }
});

var dialogVue = Vue.component("demo", {
  template: `<ejs-listview id="list" showHeader=true headerTitle='Favorite' width='200px'></ejs-listview>`,
  data() {
    return {
      data: {}
    };
  }
});
export default {
  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' },
    listTemplate: function () {
      return { template : listVue}
    },
    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'},
    dialogcontent: function () {
      return { template : dialogVue}
    },
    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: {
    createList: function(e) {
    let listElem = document.getElementById('dialog').querySelector("#list");
    let listObj = document.getElementById('dialog').querySelector("#list").ej2_instances[0];
    listObj.dataSource = this.datasource.Brooke;
    listObj.fields = this.fields;
    listObj.addEventListener('select', ()=> { this.addTag(event);});
    listObj.appendTo(listElem);
    },
    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();
    }
   },
   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);
},
removeTag: function() {
   this.$refs.listviewInstance.$el.ej2_instances[0].selectedLI.children[1].remove();
}
}
}
</script>

ListView manipulation in grid layout

In Listview, list items can be rendered in grid layout with following data manipulations.

  • Add Item

  • Remove Item

  • Sort Items

  • Filter Items

Grid Layout

In this section, we will discuss about rendering of list items in grid layout.

  • Initialize and render ListView with dataSource which will render list items in list layout.

  • Now, add the below CSS to list item. This will make list items to render in grid layout

#element .e-list-item {
        height: 100px;
        width: 100px;
        float: left;
}

In the below sample, we have rendered List items in grid layout.

Source
Preview
app.vue
<template>
  <div id="sample">
    <ejs-listview id='element' :dataSource='data' :template='listTemplate'>
    </ejs-listview>
  </div>
</template>
<style>
#element {
    display: block;
    max-width: 303px;
    margin: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}

#element .e-list-item {
    height: 100px;
    width: 100px;
    float: left;
    padding: 0;
}

#listImage {
    width: 55px;
    height: 55px;
    margin-left: 20px;
    margin-top: 20px;

}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";

Vue.use(ListViewPlugin);

var listVue = Vue.component("demo", {
  template: `<img id="listImage" src="./apple.png" alt="apple" />`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
         return {
    data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
    listTemplate: function () {
      return { template : listVue}
    },
  };
}
}
</script>

Data manipulation

In this section, we will discuss about ListView data manipulations.

Add Item

We can add list item using addItem API. This will accept array of data as argument.

 this.$refs.listViewInstance.addItem([{text: 'Apricot', id: '32'}]);

In the below sample, you can add new fruit item by clicking add button which will open dialog box with fruit name and image URL text box. After entering the item details, click the add button. This will add your new fruit item.

Remove item

We can remove list item using removeItem API. This will accept fields with id or list item element as argument.

 this.$refs.listViewInstance.removeItem({id: '32'});

In the below sample, you can remove fruit by hovering the fruit item which will show delete button and click that delete button to delete that fruit from your list.

Sort Items

Listview can be sorted either in Ascending or Descending order. To enable sorting in your ListView, set sortOrder as Ascending or Descending.

<ejs-listview sortOrder='Ascending'></ejs-listview>

We can also set sorting after component initialization.

this.$refs.listViewInstance.sortOrder = 'Ascending'

In the below sample, we have sorted fruits in Ascending order. To sort it in descending, click on sort order icon and vice versa.

Filter Items

Listview data can be filtered with the help of dataManager. After filtering the data, update ListView dataSource with filtered data.

let value = this.$refs.textboxEle.value;  //input text box value
let filteredData = new DataManager(this.listdata).executeLocal(
        new Query().where("text", "startswith", value, true)
);

listViewInstance.dataSource = filteredData;

In the below sample, we can filter fruit items with the help of search text box. This will filter fruit items based on your input. Here we used startswith of input text to filter data in DataManager.

Source
Preview
app.vue
<template>
    <div id="sample">
        <div class="headerContainer">
            <div class="e-input-group">
                <input id="search" ref='searchEle' class="e-input" type="text" placeholder="Search fruits" @keyup='onKeyUp'/>
                <span class="e-input-group-icon e-input-search"></span>
            </div>
            <ejs-button id="sort" class="e-control e-btn e-small e-round e-primary e-icon-btn" v-on:click='sortItems' title="Sort fruits" data-ripple="true">
                <span class="e-btn-icon e-icons e-sort-icon-ascending"></span>
            </ejs-button>
            <ejs-button id="add" class="e-control e-btn e-small e-round e-primary e-icon-btn" v-on:click='addItem' title="Add fruit" data-ripple="true">
                <span class="e-btn-icon e-icons e-add-icon"></span>
            </ejs-button>
            <ejs-dialog id="dialog" ref='dialogObj' width='300px' :content='content' :visible='visible' header='Add Fruit' showCloseIcon='true' :buttons='addButtons' ></ejs-dialog>
        </div>
            <ejs-listview id='element' ref='listViewInstance' :dataSource='fruitsdata' :template='listTemplate' sortOrder='Ascending' :actionComplete='wireEvents'>
            </ejs-listview>
    </div>
</template>
<style>
#listImage {
    width: 55px;
    height: 55px;
    margin-left: 25px;
}

#sample {
    max-width: 440px;
    margin: auto;
    box-shadow: 0 3px 6px lightgray;
}

.headerContainer {
    height: 48px;
    line-height: 48px;
    background: rgb(2, 120, 215);
    color: white;
    margin-bottom: 3px;
}

.headerContainer .e-input-group {
    margin-left: 20px;
    width: 200px;
    background: white;
    height: 31px;
}

.headerContainer #search {
    height: 21px;
    margin-left: 10px;
}

#listDialog .input_name {
    margin-bottom: 20px;
}

.headerContainer #add,
.headerContainer #sort {
    float: right;
    margin-right: 15px;
    margin-top: 7px;
    background: white;
    color: black
}

.headerContainer .e-input-search::before {
    font-family: 'e-icons';
    content: '\e961';
    margin-top: 3px;
}

.headerContainer .e-input-group .e-input-group-icon.e-input-search {
    padding: 0 10px 0 10px;
}

#element .e-list-item {
    height: 110px;
    width: 110px;
    float: left;
    padding: 0;
    position: relative;
    user-select: none;
}

#element .e-delete-btn {
    float: right;
    visibility: hidden;
    margin-top: -10px;
}

#element .e-delete-btn.e-btn.e-small.e-round {
    width: 2em;
    height: 2em;
}

#element .e-btn.e-small.e-round .e-btn-icon.delete-icon {
    font-size: 9px;
}

#element .e-list-item:hover .e-delete-btn {
    visibility: visible;
    background: red;
    border-radius: 50%;
}

#element .fruits {
    height: inherit;
    width: inherit;
    padding: 10px 0 10px 0;
}

#element .fruitName {
    text-align: center;
}

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

#element .delete-icon::before {
    content: '\e7fc';
    color: white;
}

.headerContainer .e-sort-icon-ascending::before {
    content: '\e840';
}

.headerContainer .e-sort-icon-descending::before {
    content: '\e83f';
}
.e-dialog.e-control.e-popup.e-popup-open {
  max-height:361px !important;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { DialogPlugin } from '@syncfusion/ej2-vue-popups';
import { closest, enableRipple } from '@syncfusion/ej2-base';
import { DataManager, Query } from "@syncfusion/ej2-data";
enableRipple(true);

Vue.use(ListViewPlugin);
Vue.use(DialogPlugin);

var listVue = Vue.component("demo", {
  template: `<div class="fruits"><div class="first"><img id="listImage" :src="data.imgUrl" alt="fruit" /><ejs-button class="delete e-control e-btn e-small e-round e-delete-btn e-primary e-icon-btn" data-ripple="true"><span class="e-btn-icon e-icons delete-icon"></span></ejs-button></div><div class="fruitName">{{data.text}}</div></div>`,
  data() {
    return {
      data: {}
    };
  }
});

var dialogVue = Vue.component("demo", {
  template: `<div id="listDialog"><div class="input_name"><label for="name">Fruit Name: </label><input id="name" class="e-input" type="text" placeholder="Enter fruit name"/></div><div><label for="imgurl">Fruit Image: </label><input id="imgurl" class="e-input" type="text" placeholder="Enter image url"/></div></div>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
        return {
    fruitsdata: [
    { text: 'Date', id: '1', imgUrl: './dates.jpg' },
    { text: 'Fig', id: '2', imgUrl: './fig.jpg' },
    { text: 'Apple', id: '3', imgUrl: './apple.png' },
    { text: 'Apricot', id: '4', imgUrl: './apricot.jpg' },
    { text: 'Grape', id: '5', imgUrl: './grape.jpg' },
    { text: 'Strawberry', id: '6', imgUrl: './strawberry.jpg' },
    { text: 'Pineapple', id: '7', imgUrl: './pineapple.jpg' },
    { text: 'Melon', id: '8', imgUrl: './melon.jpg' },
    { text: 'Lemon', id: '9', imgUrl: './lemon.jpg' },
    { text: 'Cherry', id: '10', imgUrl: './cherry.jpg' },
],
    listTemplate: function () {
      return { template : listVue};
    },
    visible: false,
     content: function () {
      return { template : dialogVue};
    },
    addButtons: [{
        click: this.dlgButtonClick, buttonModel: { content: 'Add', isPrimary: true }
    }]
  };
},
methods: {
  wireEvents: function() {
    Array.prototype.forEach.call(document.getElementsByClassName('e-delete-btn'), (ele) => {
        ele.addEventListener('click', this.onDeleteBtnClick);
    });
    },
addItem: function() {
    (document.getElementById("name")).value = "";
    (document.getElementById("imgurl")).value = "";
    this.$refs.dialogObj.show();
},
sortItems: function() {
    let ele = document.getElementById("sort").firstElementChild;
    let des = ele.classList.contains('e-sort-icon-descending') ? true : false;
    if (des) {
        ele.classList.remove('e-sort-icon-descending');
        ele.classList.add('e-sort-icon-ascending');
        this.$refs.listViewInstance.sortOrder = 'Ascending';
    } else {
        ele.classList.remove('e-sort-icon-ascending');
        ele.classList.add('e-sort-icon-descending');
        this.$refs.listViewInstance.sortOrder = 'Descending'
    }
    this.$refs.listViewInstance.dataBind();
    this.wireEvents();
},
onKeyUp: function(e) {
    let value = this.$refs.searchEle.value;
    let data = new DataManager(this.fruitsdata).executeLocal(
        new Query().where("text", "startswith", value, true)
    );
    if (!value) {
        this.$refs.listViewInstance.dataSource = this.fruitsdata.slice();
    } else {
        this.$refs.listViewInstance.dataSource = data;
        this.$refs.listViewInstance.dataBind();
    }
},
onDeleteBtnClick: function(e) {
    e.stopPropagation();
    let li = closest(e.currentTarget, '.e-list-item');
    let data = this.$refs.listViewInstance.findItem(li);
    this.$refs.listViewInstance.removeItem(data);
    new DataManager(this.fruitsdata).remove('id', { id: data.id });
},
 dlgButtonClick: function() {
    let name = (document.getElementById("name")).value;
    let url = (document.getElementById("imgurl")).value;
    let id = Math.random() * 10000;
    this.$refs.listViewInstance.addItem([{ text: name, id: id, imgUrl: url }]);
    this.fruitsdata.push({ text: name, id: id, imgUrl: url });
    this.$refs.listViewInstance.$el.ej2_instances[0].element.querySelector('[data-uid="'+ id + '"]').getElementsByClassName('e-delete-btn')[0].addEventListener('click', this.onDeleteBtnClick);
    this.$refs.dialogObj.hide();
}
}
}
</script>

ListView with Drag and Drop feature (Reorder)

In ListView component, we don’t have drag and drop support. But we can achieve this requirement using TreeView component with ListView appearance.

Drag and Drop in TreeView component was enabled by setting allowDragAndDrop to true.

<ejs-treeview id='element' :fields='fields' allowDragAndDrop='true'></ejs-treeview>

The TreeView component is used to represent hierarchical data in a tree like structure. So, list items in TreeView can be dropped to child of target element. we can prevent this behaviour by cancelling the nodeDragStop and nodeDragging events.

<ejs-treeview id='element' :fields='fields' allowDragAndDrop='true' :nodeDragging='onDragStop' :nodeDragStop='onDragStop'></ejs-treeview>

fields: { dataSource: data, id: 'id', text: 'text' },

onDragStop: function(args) {
    //Block the Child Drop operation in TreeView
   let  draggingItem = document.getElementsByClassName("e-drop-in");
    if (draggingItem.length == 1) {
        draggingItem[0].classList.add('e-no-drop');
        args.cancel = true;
    }
}

In the below sample, we have rendered draggable list items.

Source
Preview
app.vue
<template>
   <div id="sample">
    <ejs-treeview id='element' :fields='fields' allowDragAndDrop='true' :nodeDragging='onDragStop' :nodeDragStop='onDragStop'></ejs-treeview>
   </div>
</template>
<style>
#sample {
    display: block;
    max-width: 280px;
    margin: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}

#element.e-treeview .e-ul {
    padding: 0;
}

#element.e-treeview .e-list-item {
    padding: 0 16px;
}

#element.e-treeview .e-text-content {
    padding: 0;
}

#element.e-treeview .e-fullrow {
    height: 36px;
}

#element.e-treeview .e-list-text {
    line-height: 34px;
}

#element.e-treeview .e-list-item:last-child {
    margin-bottom: 9px;
}

#element.e-treeview .e-list-item:first-child {
    margin-top: 9px;
}
</style>
<script>
import Vue from "vue";
import { TreeViewPlugin } from "@syncfusion/ej2-vue-navigations";

Vue.use(TreeViewPlugin);

export default {
  data: function() {
         var  data =[
    { text: 'Hennessey Venom', id: 'list-01' },
    { text: 'Bugatti Chiron', id: 'list-02' },
    { text: 'Bugatti Veyron Super Sport', id: 'list-03' },
    { text: 'SSC Ultimate Aero', id: 'list-04' },
    { text: 'Koenigsegg CCR', id: 'list-05' },
    { text: 'McLaren F1', id: 'list-06' },
    { text: 'Aston Martin One- 77', id: 'list-07' },
    { text: 'Jaguar XJ220', id: 'list-08' },
    { text: 'McLaren P1', id: 'list-09' },
    { text: 'Ferrari LaFerrari', id: 'list-10' },
];
  return {
   fields: { dataSource: data, id: 'id', text: 'text' },
    };
  },
  methods: {
    onDragStop:function(args) {
    //Block the Child Drop operation in TreeView
    let draggingItem = document.getElementsByClassName("e-drop-in");
    if (draggingItem.length == 1) {
        draggingItem[0].classList.add('e-no-drop');
        args.cancel = true;
    }
}
  }
}
</script>

We can use anchor tag along with href attribute in our ListView template property for navigation.

var listVue = Vue.component("demo", {
  template: `<a target='_blank' v-bind:href='data.url'>{{data.name}}</a>`,
  data() {
    return {
      data: {}
    };
  }
});

In the below sample, we have rendered ListView with search engines URL.

Source
Preview
app.vue
<template>
   <div id="sample">
    <ejs-listview id='List' :dataSource='data' headerTitle='Search engines' showHeader='true' :template='anchortemplate'>
    </ejs-listview>
  </div>
</template>
<style>
 #List {
        max-width: 300px;
        margin: auto;
        border: 1px solid #dddddd;
        border-radius: 3px;
    }
    #List a {
      text-decoration: none;
    }
    #List .e-list-header {
        background: rgb(2, 120, 215);
        color: white;
        font-size: 19px;
        font-weight: 500;
    }
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";

Vue.use(ListViewPlugin);

var listVue = Vue.component("demo", {
  template: `<a target='_blank' v-bind:href='data.url'>{{data.name}}</a>`,
  data() {
    return {
      data: {}
    };
  }
});

export default {
  data: function() {
       return {
      data: [
        {name: 'Google', url: 'https://www.google.com'},
        {name: 'Bing', url: 'https://www.bing.com' },
        {name: 'Yahoo', url: 'https://www.yahoo.com'},
        {name: 'Ask.com', url: 'https://www.ask.com'},
        {name: 'AOL.com', url: 'https://www.aol.com'},
    ],
    anchortemplate: function () {
        return { template : listVue};
    },
    };
  },
}
</script>

Load HTML content via AJAX

We can set external HTML page content as template for our ListView component by making use of AJAX call.

let ajax = new Ajax('./template.html', 'GET', false);
ajax.onSuccess = (e)=>{
    this.template = e;
}

ajax.send();

In the below sample, we have rendered smartphone settings template from external HTML file.

Source
Preview
app.vue
<template>
   <div id="sample">
    <ejs-listview id='element' :dataSource='data' headerTitle='Settings' showHeader='true' :template='template'>
    </ejs-listview>
  </div>
</template>
<style>
  #element {
    display: block;
    max-width: 300px;
    margin: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}

#element .e-list-header {
    background: rgb(2, 120, 215);
    color: white;
    font-size: 19px;
    font-weight: 500;
}

#element.e-listview .e-list-item {
    padding: 10px 16px;
    height: 61px;

}

#element .settings-container .name {
    font-size: 15px;
    font-weight: 500;
    line-height: 20px;
    height: 20px;
}

#element .settings-container .description {
    line-height: 20px;
    height: 20px;
    font-size: 10px;
    font-style: italic;
    margin-top: -2px;
}
</style>
<script>
import Vue from "vue";
import { ListViewPlugin } from "@syncfusion/ej2-vue-lists";
import { Ajax } from '@syncfusion/ej2-base';

Vue.use(ListViewPlugin);

export default {
  data: function() {
    return {
      data: [
    { name: 'Network & Internet', id: '0', description: 'Wi-Fi, mobile, data usage, hotspot' },
    { name: 'Connected devices', id: '1', description: 'Bluetooth, cast, NFC' },
    { name: 'Battery', id: '2', description: '18% -4h 12m left' },
    { name: 'Display', id: '3', description: 'Wallpaper, sleep, font size' },
    { name: 'Sound', id: '4', description: 'Volume, vibration, Do Not Disturb' },
    { name: 'Storage', id: '5', description: '52% used - 15.48 GB free' }
],
    template:{}
    };
  },
  created: function() {
    let ajax = new Ajax('./template.html', 'GET', false);
    ajax.onSuccess = (e)=>{
    this.template = e;
    };
    ajax.send();
  }
}
</script>