Manipulate ListView as grid layout in Angular ListView component

12 Sep 202521 minutes to read

The ListView component can display items in a grid layout with support for data manipulations such as adding, removing, sorting, and filtering items.

Grid Layout

To render list items in a grid layout:

  1. Initialize the ListView with a dataSource
  2. Apply the following CSS to transform the list items into a grid layout:
#element .e-list-item {
    height: 100px;
    width: 100px;
    float: left;
}

Here’s a basic example of ListView with grid layout:

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { ListViewModule } from '@syncfusion/ej2-angular-lists'
import { Component, ViewChild } from '@angular/core';

@Component({
  imports: [
    ListViewModule
  ],
  standalone: true,
  selector: 'my-app',
  template: `
          <ejs-listview id='element' [dataSource]='data'>
          <ng-template #template let-data="">
          <img id="listImage" src="https://ej2.syncfusion.com/documentation/code-snippet/listview/grid-layout-cs1/apple.png" alt="apple" />
          </ng-template>
          </ejs-listview>
        `,
})

export class AppComponent {
  public data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
}
@import 'node_modules/@syncfusion/ej2-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-lists/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-buttons/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-popups/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/material.css';

#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;

}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Data Manipulation

The ListView component supports various data manipulation operations to manage list items effectively.

Add Item

Use the addItem method to add new items to the ListView. The method accepts an array of items as its argument.

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

Remove Item

To remove items, use the removeItem method. Pass either the item object with an id or the list item element as the argument.

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

Sort Items

Enable sorting by setting the sortOrder property to either ‘Ascending’ or ‘Descending’.

// In template
<ejs-listview [sortOrder]="'Ascending'" [dataSource]="data"></ejs-listview>

// In component
this.listViewInstance.sortOrder = 'Ascending'

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.textboxEle.nativeElement.value;  //input text box value
let filteredData = new DataManager(this.listdata).executeLocal(
        new Query().where("text", "startswith", value, true)
);

listViewInstance.dataSource = filteredData;

The following example demonstrates all these data manipulation features:

import { Component, ViewChild, ElementRef } from '@angular/core';
import { ListViewComponent, ListViewModule } from '@syncfusion/ej2-angular-lists';
import { DialogComponent, DialogModule } from '@syncfusion/ej2-angular-popups';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

interface FruitItem {
    text: string;
    id: string;
    imgUrl: string;
}

@Component({
    imports: [
        ListViewModule,
        DialogModule,
        ButtonModule,
        FormsModule,
        CommonModule,
    ],
    standalone: true,
    selector: 'my-app',
    template: `
          <div id="sample">
      <div class="headerContainer">
        <div class="e-input-group">
          <input id="search" #searchEle class="e-input" type="text" placeholder="Search fruits" (keyup)='onKeyUp()' [(ngModel)]="searchText"/>
          <span class="e-input-group-icon e-input-search"></span>
        </div>
        <button ejs-button id="sort" class="e-control e-btn e-small e-round e-primary e-icon-btn" (click)='sortItems()' title="Sort fruits" data-ripple="true">
          <span class="e-btn-icon e-icons" [ngClass]="{'e-sort-icon-ascending': !isDescending, 'e-sort-icon-descending': isDescending}"></span>
        </button>
        <button ejs-button id="add" class="e-control e-btn e-small e-round e-primary e-icon-btn" (click)='addItem()' title="Add fruit" data-ripple="true">
          <span class="e-btn-icon e-icons e-add-icon"></span>
        </button>
        <ejs-dialog id="dialog" #dialogObj width='300px' [visible]='false' header='Add Fruit' showCloseIcon='true' [buttons]='addButtons'>
          <ng-template #content>
            <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" [(ngModel)]="newFruitName"/>
              </div>
              <div>
                <label for="imgurl">Fruit Image: </label>
                <input id="imgurl" class="e-input" type="text" placeholder="Enter image url" [(ngModel)]="newFruitImgUrl"/>
              </div>
            </div>
          </ng-template>
        </ejs-dialog>
      </div>
      <ejs-listview id='element' #listview [dataSource]='fruitsdata' [sortOrder]='currentSortOrder'>
        <ng-template #template let-data>
          <div class="fruits">
            <div class="first">
              <img id="listImage" [src]="data.imgUrl" alt="fruit" />
              <button ejs-button class="delete e-control e-btn e-small e-round e-delete-btn e-primary e-icon-btn" (click)="onDeleteBtnClick(data)" data-ripple="true">
                <span class="e-btn-icon e-icons delete-icon"></span>
              </button>
            </div>
            <div class="fruitName"></div>
          </div>
        </ng-template>
      </ejs-listview>
    </div>
        `,
})

export class AppComponent {
    @ViewChild('listview') listViewInstance!: ListViewComponent | any;
    @ViewChild('dialogObj') dialogObj!: DialogComponent | any;
    @ViewChild('searchEle') searchEle!: ElementRef;

    public fruitsdata: FruitItem[] = [
        {
            text: 'Date',
            id: '1',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/dates.jpg',
        },
        {
            text: 'Fig',
            id: '2',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/fig.jpg',
        },
        {
            text: 'Apple',
            id: '3',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/apple.png',
        },
        {
            text: 'Apricot',
            id: '4',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/apricot.jpg',
        },
        {
            text: 'Grape',
            id: '5',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/grape.jpg',
        },
        {
            text: 'Strawberry',
            id: '6',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/strawberry.jpg',
        },
        {
            text: 'Pineapple',
            id: '7',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/pineapple.jpg',
        },
        {
            text: 'Melon',
            id: '8',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/melon.jpg',
        },
        {
            text: 'Lemon',
            id: '9',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/lemon.jpg',
        },
        {
            text: 'Cherry',
            id: '10',
            imgUrl: 'https://ej2.syncfusion.com/documentation/code-snippet/listview/manipulation-cs1/cherry.jpg',
        },
    ];

    public dataManager: DataManager;

    constructor() {
        this.dataManager = new DataManager(this.fruitsdata);
    }

    public addButtons = [
        {
            click: this.dlgButtonClick.bind(this),
            buttonModel: { content: 'Add', isPrimary: true },
        },
    ];

    public isDescending = false;
    public currentSortOrder = 'Ascending';
    public searchText = '';
    public newFruitName = '';
    public newFruitImgUrl = '';

    addItem() {
        this.newFruitName = '';
        this.newFruitImgUrl = '';
        this.dialogObj.show();
    }

    sortItems() {
        this.isDescending = !this.isDescending;
        this.currentSortOrder = this.isDescending ? 'Descending' : 'Ascending';
        if (this.listViewInstance) {
            this.listViewInstance.sortOrder = this.currentSortOrder;
        }
    }

    onKeyUp() {
        if (this.listViewInstance) {
            if (this.searchText) {
                this.listViewInstance.dataSource = new DataManager(
                    this.fruitsdata
                ).executeLocal(
                    new Query().where('text', 'startswith', this.searchText, true)
                );
            } else {
                this.listViewInstance.dataSource = this.dataManager;
            }
        }
    }

    onDeleteBtnClick(data: FruitItem) {
        if (this.listViewInstance) {
            this.listViewInstance.removeItem(data);
        }
        this.fruitsdata = this.fruitsdata.filter((item) => item.id !== data.id);
        this.dataManager = new DataManager(this.fruitsdata);
    }

    dlgButtonClick() {
        const id = Math.random().toString(36).substr(2, 9);
        const newFruit: FruitItem = {
            text: this.newFruitName,
            id: id,
            imgUrl: this.newFruitImgUrl,
        };
        if (this.listViewInstance) {
            this.listViewInstance.addItem([newFruit]);
        }
        this.fruitsdata.push(newFruit);
        this.dataManager = new DataManager(this.fruitsdata);
        this.dialogObj.hide();
    }
}
@import 'node_modules/@syncfusion/ej2-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-lists/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-buttons/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-popups/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/material.css';

#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;
}

/* csslint ignore:start */
.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;
}

/* csslint ignore:end */
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));