
Syncfusion Angular Listview
- 162 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-listview for development tasks
About
syncfusion-angular-listview: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-listview
Syncfusion Angular Listview by the numbers
- 162 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,378 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-listviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 162 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-listview for development tasks
Files
Implementing Syncfusion Angular ListView
The Syncfusion Angular ListView component is a feature-rich, interactive component for displaying data in list format. It provides built-in support for data binding, grouping, nested lists, custom templates, selection modes, drag-and-drop, virtualization for large datasets, and comprehensive accessibility features. The component is production-ready and suitable for creating modern, data-driven user interfaces.
When to Use This Skill
Use this skill when:
- Building interactive list-based interfaces with Angular
- Displaying data from local or remote sources
- Creating grouped or nested list structures
- Customizing list item appearance with templates
- Handling item selection and user interactions
- Implementing specialized patterns (chat windows, checklists, dual-lists)
- Optimizing performance with virtualization for large datasets
- Building accessible list interfaces
Component Overview
Package: @syncfusion/ej2-angular-lists
Key Capabilities:
- ✅ Local and remote data binding (arrays, DataManager, OData, REST APIs)
- ✅ Grouping and nested list hierarchies
- ✅ Flexible item, header, and group templates
- ✅ Single/multiple selection modes with checkboxes
- ✅ Full event system (select, click, delete, add, remove)
- ✅ Drag-and-drop for item reordering
- ✅ Virtualization for large datasets (1000+ items)
- ✅ AJAX content loading and dynamic templates
- ✅ Integrated paging support
- ✅ Comprehensive theming and styling options
- ✅ WCAG accessibility compliance
---
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Angular CLI setup and project configuration
- Installing @syncfusion/ej2-angular-lists package
- Adding CSS themes and styles
- Creating your first ListView component
- Basic data binding with minimal examples
- Running the application (ng serve)
Data Binding
📄 Read: references/data-binding.md
- Binding local data arrays (strings, objects)
- Field configuration and data mapping
- Remote data binding with DataManager
- OData and REST API integration
- Dynamic data updates and refresh strategies
- Field properties: id, text, isChecked, enabled, tooltip, groupBy
Customization and Templates
📄 Read: references/customization-and-templates.md
- Header template customization with buttons and search bars
- Item templates with avatars, badges, multi-line layouts
- Group header templates with dynamic content
- Dynamic templates based on device or screen size
- Built-in CSS classes (e-list-template, e-list-wrapper, e-list-avatar, etc.)
- Advanced template patterns with data binding
Grouping and Nested Lists
📄 Read: references/grouping-and-nested-lists.md
- Grouping items by category with groupBy field
- Customizing group headers and templates
- Creating nested list structures for hierarchical data
- Child data binding and expandable groups
- Group header customization with item counts
Selection and Item Management
📄 Read: references/selection-and-items.md
- Selection modes (single, multiple, checkbox)
- Getting selected items with getSelectedItems() method
- Adding items dynamically with addItem()
- Removing items with removeItem()
- Event handling (select, actionComplete)
- Programmatic selection and deselection
Advanced Features
📄 Read: references/advanced-features.md
- Virtualization for high-performance large datasets
- Scrolling and scroll position management
- Drag-and-drop for item reordering
- Filtering and searching list items
- Integrating pager component with ListView
- Loading states and spinners during data fetch
- AJAX content loading into list items
Specialized Use Cases
📄 Read: references/specialized-use-cases.md
- Building chat window layouts
- Creating checklist interfaces with checkboxes
- Building dual-list (transfer list) components
- Creating grid-based layouts with ListView
- Rendering hyperlinked navigation lists
- Customizing with dynamic tags and badges
- Mobile contact layout patterns
Styling and Themes
📄 Read: references/styling-and-themes.md
- Theme imports (Material3, Bootstrap, Fabric, Tailwind)
- CSS class customization and overrides
- Custom styling for list items and groups
- Responsive design and mobile optimization
- Animation settings and transitions
- RTL (right-to-left) support
- Dark mode theming
Item Count and Group Headers
📄 Read: references/item-count-and-grouping.md
- Displaying item count in group headers
- Dynamic count calculation and updates
- Group statistics and aggregations
- Advanced group header templates
- Conditional item count display
Accessibility
📄 Read: references/accessibility.md
- WCAG 2.1 compliance and keyboard navigation
- ARIA attributes and screen reader support
- Focus management and indicators
- Keyboard shortcuts and navigation patterns
- Color contrast and accessible theming
- Testing accessibility with assistive technologies
Complete API Reference
📄 Read: references/api-reference.md
- Properties: animation, enablePersistence, enableRtl, locale, query, and more
- Methods: back(), checkAllItems(), selectMultipleItems(), and complete method suite
- Events: select, scroll, actionBegin, actionComplete, actionFailure with argument details
- Complete working examples for each API
- Quick reference tables for properties, methods, and events
---
Quick Start Example
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='sample-list'
[dataSource]='data'>
</ejs-listview>
`
})
export class AppComponent {
// Simple string data
public data: string[] = [
'Artwork', 'Abstract', 'Modern Painting',
'Ceramics', 'Animation Art', 'Oil Painting'
];
}CSS Import (in styles.css):
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-angular-lists/styles/material3.css";Run:
npm install @syncfusion/ej2-angular-lists --save
ng serve --open---
Common Patterns
Pattern 1: Data-Driven List with Field Mapping
Use when displaying complex objects with multiple properties:
public data = [
{ name: 'John', email: 'john@example.com', id: '1' },
{ name: 'Jane', email: 'jane@example.com', id: '2' }
];
public fields = { text: 'name', id: 'id' };Pattern 2: Grouped List
Use when organizing items by category:
public fields = { text: 'name', groupBy: 'department' };Pattern 3: Templated List
Use when you need custom layouts:
<ejs-listview [dataSource]='data' cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper">
<span>{{ data.name }}</span>
</div>
</ng-template>
</ejs-listview>Pattern 4: Selection with Checkboxes
Use when users need to select multiple items:
<ejs-listview [dataSource]='data' [showCheckBox]='true'></ejs-listview>Pattern 5: Dynamic Add/Remove
Use for interactive list management:
addItem() {
this.listview.addItem([{ text: 'New Item', id: 'new' }]);
}
removeItem(element: HTMLElement) {
this.listview.removeItem(element);
}---
Key Props and Configuration
| Property | Type | Description |
|---|---|---|
dataSource | array/DataManager | The data to display in the list |
fields | object | Field mappings (text, id, groupBy, etc.) |
template | string/function | Custom template for list items |
headerTemplate | string/function | Custom header template |
groupTemplate | string/function | Custom group header template |
showCheckBox | boolean | Show checkboxes for selection (default: false) |
showHeader | boolean | Display ListView header (default: false) |
headerTitle | string | Title for the header |
enableVirtualization | boolean | Enable virtual scrolling for large datasets |
enableHtmlSanitizer | boolean | Sanitize HTML content (default: true) |
allowDragAndDrop | boolean | Enable drag-and-drop reordering |
select | event | Triggered when item is selected |
actionComplete | event | Triggered after add/remove operations |
---
Common Use Cases
1. Task Manager - Dynamic add/remove with checkboxes and drag-drop 2. Contact Directory - Multi-line templates with grouping by department 3. Chat Interface - Custom templates with avatars and timestamps 4. Product Catalog - Virtualization for thousands of products 5. Navigation Menu - Nested lists with icons and routing 6. Checklist - Checkbox selection with count in headers 7. Dual-List Transfer - Two ListViews with move operations 8. Mobile Navigation - Touch-friendly, responsive list layout
---
Next Steps
- Start here: Getting Started
- Bind data: Data Binding
- Build interactive: Selection and Items
- Optimize performance: Advanced Features
- Advanced patterns: Specialized Use Cases
For API reference and detailed documentation, see the Syncfusion Angular ListView API.
Accessibility in Syncfusion Angular ListView
WCAG 2.1 Compliance
The Syncfusion ListView component follows Web Content Accessibility Guidelines (WCAG) 2.1 Level AA standards, ensuring it's accessible to users with disabilities.
---
Keyboard Navigation
Built-in Keyboard Support
ListView automatically supports these keyboard shortcuts:
| Key | Action |
|---|---|
Arrow Up | Move focus to previous item |
Arrow Down | Move focus to next item |
Home | Move focus to first item |
End | Move focus to last item |
Space | Select/deselect item (with checkboxes) |
Enter | Activate/select focused item |
Ctrl+A | Select all items (with checkboxes) |
Example: Keyboard-Accessible ListView
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-accessible-list',
template: `
<ejs-listview
id='accessible-list'
[dataSource]='items'
[showCheckBox]='true'
role='listbox'
aria-label='Accessible list of items'>
</ejs-listview>
`
})
export class AccessibleListComponent {
public items = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
}---
ARIA Attributes
ListView includes proper ARIA attributes for screen reader support:
<ejs-listview
id='products-list'
[dataSource]='products'
role='listbox'
aria-label='List of available products'
aria-describedby='list-description'>
</ejs-listview>
<p id='list-description'>Select products from the list below</p>ARIA Roles and Properties
| Attribute | Purpose |
|---|---|
role="listbox" | Identifies the component as a list |
aria-label | Provides accessible name for the list |
aria-describedby | Links to descriptive text |
aria-selected | Indicates selected state |
aria-disabled | Indicates disabled items |
aria-checked | Indicates checkbox state |
aria-live="polite" | Announces dynamic content changes |
---
Screen Reader Support
Making ListView Screen-Reader Friendly
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-sr-friendly-list',
template: `
<div>
<label for='item-list' class='sr-label'>
Select items from the list
</label>
<ejs-listview
id='item-list'
[dataSource]='items'
[showCheckBox]='true'
role='listbox'
aria-label='Item selection list'
[fields]='fields'>
</ejs-listview>
</div>
`,
styles: [`
/* Hide label visually but keep it for screen readers */
.sr-label {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
`]
})
export class ScreenReaderFriendlyComponent {
public items = [
{ text: 'Apple', id: '1' },
{ text: 'Banana', id: '2' },
{ text: 'Orange', id: '3' }
];
public fields = {
id: 'id',
isChecked: 'isChecked'
};
}---
Focus Management
Visible Focus Indicators
/* Enhance focus visibility for keyboard navigation */
.e-listview .e-list-item:focus,
.e-listview .e-list-item:focus-visible {
outline: 2px solid #007bff;
outline-offset: -2px;
}
/* High contrast mode support */
@media (prefers-contrast: more) {
.e-listview .e-list-item:focus {
outline-width: 3px;
outline-color: #000;
}
}Programmatic Focus Management
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild, ElementRef } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-focus-management',
template: `
<button (click)="focusFirstItem()">Focus First Item</button>
<button (click)="focusLastItem()">Focus Last Item</button>
<ejs-listview
#listview
id='focus-list'
[dataSource]='items'
(select)="onSelect($event)">
</ejs-listview>
`
})
export class FocusManagementComponent {
@ViewChild('listview', { read: ElementRef }) listView!: ElementRef;
public items = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
focusFirstItem() {
const firstItem = this.listView.nativeElement.querySelector('.e-list-item');
if (firstItem) {
firstItem.focus();
}
}
focusLastItem() {
const items = this.listView.nativeElement.querySelectorAll('.e-list-item');
if (items.length > 0) {
items[items.length - 1].focus();
}
}
onSelect(event: any) {
// Announce selection to screen readers
const announcement = `${event.data.text} selected`;
this.announceToScreenReader(announcement);
}
announceToScreenReader(message: string) {
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.className = 'sr-only';
announcement.textContent = message;
document.body.appendChild(announcement);
setTimeout(() => announcement.remove(), 1000);
}
}---
Color Contrast
Ensuring Sufficient Contrast
/* Meet WCAG AA standards (4.5:1 for text, 3:1 for graphics) */
.e-listview .e-list-item {
color: #212121; /* Dark text */
background-color: #ffffff; /* Light background */
}
.e-listview .e-list-item:hover {
background-color: #e3f2fd; /* Sufficient contrast */
}
.e-listview .e-list-item.e-active {
background-color: #0052cc;
color: #ffffff; /* Sufficient contrast */
}
/* Ensure icons have sufficient contrast */
.e-listview .e-icon {
color: #212121; /* Dark icon */
}Testing Contrast Ratios
Use WebAIM's Contrast Checker or axe DevTools to verify contrast ratios meet WCAG requirements.
---
Text Alternatives
Providing Text for Visual Content
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-text-alternatives',
template: `
<ejs-listview
[dataSource]='items'
cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper">
<!-- Image with alt text -->
<img
[src]="data.imageUrl"
[alt]="data.imageAlt"
class="item-image">
<!-- Text description -->
<span class="item-text">{{data.name}}</span>
<!-- Icon with aria-label -->
<span
class="item-icon"
[ngClass]="data.iconClass"
[attr.aria-label]="data.iconDescription">
</span>
</div>
</ng-template>
</ejs-listview>
`
})
export class TextAlternativesComponent {
public items = [
{
id: '1',
name: 'Product A',
imageUrl: 'product-a.jpg',
imageAlt: 'Product A - a blue widget',
iconClass: 'e-icons e-add-icon',
iconDescription: 'Add product to cart'
},
{
id: '2',
name: 'Product B',
imageUrl: 'product-b.jpg',
imageAlt: 'Product B - a red gadget',
iconClass: 'e-icons e-delete-icon',
iconDescription: 'Remove product from list'
}
];
}---
Testing Accessibility
Automated Testing
// Example using axe accessibility testing
import { axe, toHaveNoViolations } from 'jest-axe';
describe('ListView Accessibility', () => {
it('should not have any accessibility violations', async () => {
const results = await axe(document);
expect(results).toHaveNoViolations();
});
});Manual Testing Checklist
- [ ] Test keyboard navigation (Tab, Arrow keys, Enter, Space)
- [ ] Use screen reader (NVDA, JAWS, VoiceOver) to verify announcements
- [ ] Check color contrast with accessibility tools (WebAIM, Contrast Checker)
- [ ] Verify focus indicators are visible
- [ ] Test with browser zoom at 200%
- [ ] Verify text alternatives for all images/icons
- [ ] Check for keyboard traps
- [ ] Test with high contrast mode enabled
---
Best Practices
✅ Always provide meaningful ARIA labels ✅ Ensure keyboard navigation works smoothly ✅ Use semantic HTML elements ✅ Maintain sufficient color contrast ratios ✅ Provide text alternatives for visual content ✅ Test with actual assistive technology ✅ Document accessibility features ✅ Follow WCAG 2.1 Level AA guidelines ✅ Keep focus indicators visible ✅ Use screen reader testing tools
---
Resources
Advanced Features in Syncfusion Angular ListView
Table of Contents
---
Virtualization
Module Injection: VirtualizationService
To use UI virtualization, you must inject the VirtualizationService into your component's providers:
For Standalone Components (Angular 14+):
import { ListViewModule, VirtualizationService } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
providers: [VirtualizationService], // ← Required for virtualization
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='virtual-list'
[dataSource]='largeDataset'
[enableVirtualization]='true'
[height]='400'>
</ejs-listview>
`
})
export class AppComponent {
public largeDataset: any[] = [];
constructor() {
this.generateLargeDataset();
}
generateLargeDataset() {
for (let i = 1; i <= 5000; i++) {
this.largeDataset.push({
text: `Item ${i}`,
id: i.toString()
});
}
}
}For NgModule-based Projects (Angular 13 and below):
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ListViewModule, VirtualizationService } from '@syncfusion/ej2-angular-lists';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ListViewModule],
providers: [VirtualizationService], // ← Inject VirtualizationService
bootstrap: [AppComponent]
})
export class AppModule { }Enable Virtualization for Large Datasets
Virtualization renders only visible items, dramatically improving performance:
<ejs-listview
[dataSource]='largeDataset'
[enableVirtualization]='true'
[itemHeight]='50'
[height]='500'
[fields]='fields'>
</ejs-listview>Key properties:
enableVirtualization: boolean - Enable/disable virtualizationitemHeight: number - Height of each item (required for calculation, default: 41px)height: number/string - ListView container height (enables container scrolling)
Virtual Scroll vs Window Scroll
Window Scroll (Default - entire browser window scrolls):
<ejs-listview
[dataSource]='data'
[enableVirtualization]='true'>
<!-- No height property set -->
</ejs-listview>Container Scroll (Dedicated scrollable container):
<ejs-listview
[dataSource]='data'
[enableVirtualization]='true'
[height]='500'> <!-- ← Creates dedicated scrollable container -->
</ejs-listview>Performance Optimization with Virtualization
import { ListViewModule, VirtualizationService } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
providers: [VirtualizationService],
standalone: true,
template: `
<ejs-listview
id='optimized-list'
[dataSource]='largeDataset'
[enableVirtualization]='true'
[itemHeight]='48'
[height]='600'
[fields]='fields'>
<ng-template #template let-data>
<div class="list-item">
<span>{{data.text}}</span>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.list-item {
height: 48px;
padding: 8px;
display: flex;
align-items: center;
}
`]
})
export class AppComponent {
public largeDataset: any[] = [];
public fields = { text: 'text', id: 'id' };
ngOnInit() {
// Generate 10,000 items
for (let i = 1; i <= 10000; i++) {
this.largeDataset.push({
id: i.toString(),
text: `Product ${i}`,
price: Math.random() * 1000
});
}
}
}Best practices:
- Set
itemHeightto exact pixel value matching your template - Set
heightto container's visible height - Use simple templates for better performance
- Keep item data minimal (no complex objects)
- Batch API calls for loading more data
Use virtualization when:
- Dataset has 500+ items
- Need to display 10,000+ items efficiently
- Performance is critical
- Mobile/low-resource devices
- Large DOM would cause memory issues
---
Scrolling
Basic Scrolling
<ejs-listview
[dataSource]='data'
[height]='400'
style="overflow-y: auto;">
</ejs-listview>Scroll to Item Programmatically
scrollToItem(itemIndex: number) {
const items = document.querySelectorAll('.e-list-item');
if (items[itemIndex]) {
items[itemIndex].scrollIntoView({ behavior: 'smooth' });
}
}Scroll to End
scrollToEnd() {
const listView = document.getElementById('sample-list');
if (listView) {
listView.scrollTop = listView.scrollHeight;
}
}Scroll Event Handler
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='data'
(scroll)="onScroll($event)">
</ejs-listview>
`
})
export class AppComponent {
public data: any[] = [];
onScroll(event: any) {
console.log('Scroll position:', event.distanceY);
// Load more data when near bottom
if (event.distanceY > 90) {
this.loadMoreData();
}
}
loadMoreData() {
// Fetch additional data from API
}
}---
Drag and Drop
Enable Drag and Drop Reordering
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='draggable-list'
[dataSource]='items'
[allowDragAndDrop]='true'>
</ejs-listview>
`
})
export class AppComponent {
public items = [
{ text: 'Task 1', id: '1' },
{ text: 'Task 2', id: '2' },
{ text: 'Task 3', id: '3' },
{ text: 'Task 4', id: '4' },
{ text: 'Task 5', id: '5' }
];
}Two-Way Drag and Drop Between Lists
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<div style="display: flex; gap: 20px;">
<div>
<h3>Available</h3>
<ejs-listview
#availableList
[dataSource]='availableItems'
[allowDragAndDrop]='true'
[scope]='scope'>
</ejs-listview>
</div>
<div>
<h3>Selected</h3>
<ejs-listview
#selectedList
[dataSource]='selectedItems'
[allowDragAndDrop]='true'
[scope]='scope'>
</ejs-listview>
</div>
</div>
`
})
export class AppComponent {
@ViewChild('availableList') availableList!: ListViewComponent;
@ViewChild('selectedList') selectedList!: ListViewComponent;
public scope: string = 'shared-scope';
public availableItems = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
public selectedItems: any[] = [];
}---
Filtering
Filter List Items
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<input
type="text"
placeholder="Search items..."
(input)="onFilter($event)"
class="search-box">
<ejs-listview
#listview
[dataSource]='filteredData'>
</ejs-listview>
`,
styles: [`
.search-box {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
`]
})
export class AppComponent {
@ViewChild('listview') listViewInstance!: ListViewComponent;
public allData = [
{ text: 'Apple', id: '1' },
{ text: 'Apricot', id: '2' },
{ text: 'Banana', id: '3' },
{ text: 'Blueberry', id: '4' },
{ text: 'Cherry', id: '5' }
];
public filteredData: any[] = [...this.allData];
onFilter(event: any) {
const searchValue = event.target.value.toLowerCase();
this.filteredData = this.allData.filter(item =>
item.text.toLowerCase().includes(searchValue)
);
}
}Advanced Filtering with Multiple Criteria
filterByMultipleCriteria(filters: { [key: string]: any }) {
this.filteredData = this.allData.filter(item => {
return Object.keys(filters).every(key =>
item[key].toString().toLowerCase()
.includes(filters[key].toLowerCase())
);
});
}---
Paging Integration
Integrate with Pager Component
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { PagerModule } from '@syncfusion/ej2-angular-grids';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule, PagerModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-pager
#pager
[pageSize]='pageSize'
[pageCount]='pageCount'
(click)="onPageChange($event)">
</ejs-pager>
<ejs-listview
[dataSource]='currentPageData'>
</ejs-listview>
`
})
export class AppComponent {
public allData: any[] = [];
public pageSize: number = 10;
public currentPage: number = 1;
public pageCount: number = 0;
public currentPageData: any[] = [];
constructor() {
this.loadData();
}
loadData() {
// Load all data
for (let i = 1; i <= 100; i++) {
this.allData.push({ text: `Item ${i}`, id: i.toString() });
}
this.pageCount = Math.ceil(this.allData.length / this.pageSize);
this.updatePageData();
}
onPageChange(pageNumber: number) {
this.currentPage = pageNumber;
this.updatePageData();
}
updatePageData() {
const startIndex = (this.currentPage - 1) * this.pageSize;
this.currentPageData = this.allData.slice(
startIndex,
startIndex + this.pageSize
);
}
}---
Loading States
Display Spinner During Data Load
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ListViewModule, CommonModule],
standalone: true,
selector: 'app-root',
template: `
<div *ngIf="isLoading" class="spinner-container">
<div class="spinner"></div>
<p>Loading items...</p>
</div>
<ejs-listview
*ngIf="!isLoading"
[dataSource]='data'>
</ejs-listview>
`,
styles: [`
.spinner-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`]
})
export class AppComponent {
public data: any[] = [];
public isLoading: boolean = true;
constructor() {
this.loadData();
}
loadData() {
// Simulate API call
setTimeout(() => {
this.data = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
this.isLoading = false;
}, 2000);
}
}---
AJAX Content Loading
Load HTML Content into List Items
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='ajax-list'
[dataSource]='data'
cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper">
<span class="e-list-content" [innerHTML]="data.htmlContent"></span>
</div>
</ng-template>
</ejs-listview>
`
})
export class AppComponent {
public data: any[] = [];
constructor(
private http: HttpClient,
private sanitizer: DomSanitizer
) {
this.loadAjaxContent();
}
loadAjaxContent() {
const items = [
{ id: '1', title: 'Content 1', url: '/api/content/1' },
{ id: '2', title: 'Content 2', url: '/api/content/2' },
{ id: '3', title: 'Content 3', url: '/api/content/3' }
];
items.forEach(item => {
this.http.get(item.url, { responseType: 'text' }).subscribe(
(content) => {
this.data.push({
id: item.id,
title: item.title,
htmlContent: this.sanitizer.sanitize(
1, // SecurityContext.HTML
content
)
});
}
);
});
}
}---
Performance Best Practices
✅ Enable virtualization for 500+ items ✅ Use appropriate itemHeight for virtualization ✅ Lazy load data as user scrolls ✅ Implement debouncing for filter/search ✅ Use OnPush change detection for large lists ✅ Avoid complex templates with heavy computations ✅ Cache computed properties ✅ Test performance with realistic dataset sizes ✅ Monitor memory usage with large datasets ✅ Use web workers for data processing if needed
Complete API Reference - Properties, Methods & Events
Purpose: This document provides comprehensive API reference covering all properties, methods, and events for the ListView component. All examples are production-ready with proper imports and error handling.
---
Table of Contents
---
Properties Reference
animation Property
Type: AnimationSettings Default: { effect: 'SlideLeft', duration: 400, easing: 'ease' }
The animation property controls the animation applied when transitioning between list views or expanding nested lists.
Available Animation Effects:
SlideLeft- Slides in from leftSlideRight- Slides in from rightZoom- Zoom effectFade- Fade transitionNone- No animation
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-animation-list',
template: `
<ejs-listview
id='animation-list'
[dataSource]='data'
[animation]='animationSettings'
[headerTitle]='title'
[showHeader]='true'>
</ejs-listview>
`
})
export class AnimationListComponent {
public data = [
{ text: 'Accounts', id: '1' },
{ text: 'Audit', id: '2' },
{ text: 'Customer Relations', id: '3' }
];
public animationSettings = {
effect: 'Zoom', // Animation effect
duration: 600, // Duration in ms
easing: 'ease-in-out' // Easing function
};
public title = 'Departments';
}---
enablePersistence Property
Type: boolean Default: false
Persists the ListView's state (selected items, scroll position, etc.) in localStorage between page reloads.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-persistent-list',
template: `
<ejs-listview
id='persist-list'
[dataSource]='data'
[enablePersistence]='true'
[showCheckBox]='true'>
</ejs-listview>
`
})
export class PersistentListComponent {
public data = [
{ text: 'Option 1', id: '1', isChecked: false },
{ text: 'Option 2', id: '2', isChecked: false },
{ text: 'Option 3', id: '3', isChecked: false }
];
}Use Case: Shopping carts, favorite lists, saved selections that should survive page refreshes.
---
enableRtl Property
Type: boolean Default: false
Enables right-to-left (RTL) text direction for languages like Arabic, Hebrew, Farsi.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-rtl-list',
template: `
<div [dir]="isArabic ? 'rtl' : 'ltr'">
<ejs-listview
id='rtl-list'
[dataSource]='data'
[enableRtl]='isArabic'>
</ejs-listview>
</div>
`,
styles: [`
:host ::ng-deep .e-listview.e-rtl {
direction: rtl;
text-align: right;
}
`]
})
export class RtlListComponent {
public isArabic = true;
public data = [
{ text: 'مرحبا', id: '1' },
{ text: 'السلام', id: '2' },
{ text: 'عليكم', id: '3' }
];
}---
locale Property
Type: string Default: 'en-US'
Sets the locale for localized text and number formatting in the ListView.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-locale-list',
template: `
<ejs-listview
id='locale-list'
[dataSource]='data'
[locale]='locale'>
</ejs-listview>
`
})
export class LocaleListComponent {
public locale = 'de-DE'; // German locale
public data = [
{ text: 'Januar', id: '1' },
{ text: 'Februar', id: '2' },
{ text: 'März', id: '3' }
];
}---
query Property
Type: Query Default: null
Filters and selects data from a remote DataManager using OData or REST API queries.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { DataManager, Query, ODataV4Adaptor } from '@syncfusion/ej2-data';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-query-list',
template: `
<ejs-listview
id='query-list'
[dataSource]='dataManager'
[query]='query'
[fields]='fields'
[headerTitle]='title'
[showHeader]='true'>
</ejs-listview>
`
})
export class QueryListComponent {
// Remote OData service
public dataManager = new DataManager({
url: 'https://js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/',
adaptor: new ODataV4Adaptor(),
crossDomain: true
});
// Query to filter and select specific columns
public query = new Query()
.from('Products')
.select('ProductID,ProductName,UnitPrice')
.where('UnitPrice', 'greaterThan', 20)
.take(10);
public fields = {
id: 'ProductID',
text: 'ProductName'
};
public title = 'Products Over $20';
}---
width & height Properties
Type: number | string Default: ''
Sets explicit dimensions for the ListView container. Enables dedicated scrolling when both are set.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-sized-list',
template: `
<ejs-listview
id='sized-list'
[dataSource]='data'
[width]='400'
[height]='500'
[enableVirtualization]='true'>
</ejs-listview>
`
})
export class SizedListComponent {
public data = Array.from({ length: 1000 }, (_, i) => ({
text: `Item ${i + 1}`,
id: i.toString()
}));
}---
sortOrder Property
Type: SortOrder (enum) Values: None | Ascending | Descending Default: 'None'
Sorts the list data in ascending or descending order by the field specified in fields.sortBy.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-sorted-list',
template: `
<div>
<button (click)="changeSortOrder('Ascending')">Sort A-Z</button>
<button (click)="changeSortOrder('Descending')">Sort Z-A</button>
<ejs-listview
id='sorted-list'
[dataSource]='data'
[sortOrder]='sortOrder'
[fields]='fields'>
</ejs-listview>
</div>
`
})
export class SortedListComponent {
public sortOrder: string = 'Ascending';
public data = [
{ name: 'Zebra', id: '1' },
{ name: 'Apple', id: '2' },
{ name: 'Mango', id: '3' },
{ name: 'Banana', id: '4' }
];
public fields = {
text: 'name',
id: 'id',
sortBy: 'name'
};
changeSortOrder(order: string) {
this.sortOrder = order;
}
}---
cssClass Property
Type: string Default: ''
Adds custom CSS classes to the ListView root element for styling and functionality customization.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-custom-styled-list',
template: `
<ejs-listview
id='styled-list'
[dataSource]='data'
cssClass='custom-list compact-mode'>
</ejs-listview>
`,
styles: [`
:host ::ng-deep .custom-list {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
:host ::ng-deep .custom-list.compact-mode .e-list-item {
padding: 4px 8px;
font-size: 12px;
}
:host ::ng-deep .custom-list .e-list-item:hover {
background-color: #f0f0f0;
}
`]
})
export class CustomStyledListComponent {
public data = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' },
{ text: 'Item 3', id: '3' }
];
}---
htmlAttributes Property
Type: object Default: {}
Adds custom HTML attributes (id, class, data-*, etc.) to the ListView element.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-attr-list',
template: `
<ejs-listview
id='attr-list'
[dataSource]='data'
[htmlAttributes]='customAttrs'>
</ejs-listview>
`
})
export class AttrListComponent {
public data = [
{ text: 'Home', id: '1' },
{ text: 'Profile', id: '2' },
{ text: 'Settings', id: '3' }
];
public customAttrs = {
'data-testid': 'navigation-list',
'aria-label': 'Main navigation',
'role': 'navigation'
};
}---
Methods Reference
back() Method
Returns: void
Navigates back from a nested list to the parent list.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-nested-nav',
template: `
<button (click)="goBack()" *ngIf="isInNestedList">← Back</button>
<ejs-listview
#listview
id='nested-list'
[dataSource]='data'
(select)='onSelect($event)'>
</ejs-listview>
`
})
export class NestedNavComponent {
@ViewChild('listview') listViewInstance?: ListViewComponent;
public isInNestedList = false;
public data = [
{
text: 'Accounts',
id: '1',
child: [
{ text: 'Checking', id: '1-1' },
{ text: 'Savings', id: '1-2' }
]
},
{
text: 'Downloads',
id: '2',
child: [
{ text: 'Documents', id: '2-1' },
{ text: 'Images', id: '2-2' }
]
}
];
onSelect(event: any) {
// When user selects parent with children, they navigate into nested list
this.isInNestedList = !!event.data.child;
}
goBack() {
if (this.listViewInstance) {
this.listViewInstance.back();
this.isInNestedList = false;
}
}
}---
checkAllItems() Method
Returns: void
Checks all items in the ListView when checkboxes are enabled.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-check-all',
template: `
<div>
<button (click)="selectAll()">Check All</button>
<button (click)="deselectAll()">Uncheck All</button>
</div>
<ejs-listview
#checklist
id='checklist'
[dataSource]='tasks'
[showCheckBox]='true'
[fields]='fields'>
</ejs-listview>
`
})
export class CheckAllComponent {
@ViewChild('checklist') checklistInstance?: ListViewComponent;
public tasks = [
{ text: 'Review Code', id: '1', isChecked: false },
{ text: 'Write Tests', id: '2', isChecked: false },
{ text: 'Deploy', id: '3', isChecked: false }
];
public fields = { isChecked: 'isChecked' };
selectAll() {
if (this.checklistInstance) {
this.checklistInstance.checkAllItems();
}
}
deselectAll() {
if (this.checklistInstance) {
this.checklistInstance.uncheckAllItems();
}
}
}---
checkItem(item) & uncheckItem(item) Methods
Parameter: item - Fields | HTMLElement | Element Returns: void
Checks or unchecks a specific list item programmatically.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-manual-check',
template: `
<button (click)="toggleItemById('1')">Toggle Item 1</button>
<button (click)="toggleItemById('2')">Toggle Item 2</button>
<ejs-listview
#itemlist
id='item-list'
[dataSource]='items'
[showCheckBox]='true'
[fields]='fields'>
</ejs-listview>
`
})
export class ManualCheckComponent {
@ViewChild('itemlist') itemlistInstance?: ListViewComponent;
public items = [
{ text: 'JavaScript', id: '1', isChecked: false },
{ text: 'TypeScript', id: '2', isChecked: true },
{ text: 'Angular', id: '3', isChecked: false }
];
public fields = { isChecked: 'isChecked' };
toggleItemById(id: string) {
if (!this.itemlistInstance) return;
const element = this.itemlistInstance.element.querySelector(
`[data-uid="${id}"]`
) as HTMLElement;
if (element) {
const isChecked = element.classList.contains('e-checked');
if (isChecked) {
this.itemlistInstance.uncheckItem(element);
} else {
this.itemlistInstance.checkItem(element);
}
}
}
}---
disableItem(item) & enableItem(item) Methods
Parameter: item - Fields | HTMLElement | Element Returns: void
Disables or enables specific list items (grayed out, non-interactive).
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-disable-items',
template: `
<button (click)="toggleDisable('1')">Toggle Item 1</button>
<ejs-listview
#disablelist
id='disable-list'
[dataSource]='items'
[fields]='fields'>
</ejs-listview>
`,
styles: [`
:host ::ng-deep .e-list-item.e-disabled {
opacity: 0.5;
cursor: not-allowed;
color: #999;
}
`]
})
export class DisableItemsComponent {
@ViewChild('disablelist') disablelistInstance?: ListViewComponent;
public items = [
{ text: 'Active Item', id: '1', enabled: true },
{ text: 'Another Active', id: '2', enabled: true },
{ text: 'Locked Item', id: '3', enabled: false }
];
public fields = { enabled: 'enabled' };
toggleDisable(id: string) {
if (!this.disablelistInstance) return;
const element = this.disablelistInstance.element.querySelector(
`[data-uid="${id}"]`
) as HTMLElement;
if (element) {
const isDisabled = element.classList.contains('e-disabled');
if (isDisabled) {
this.disablelistInstance.enableItem(element);
} else {
this.disablelistInstance.disableItem(element);
}
}
}
}---
findItem(item) Method
Parameter: item - Fields | HTMLElement | Element Returns: SelectedItem
Finds an item and returns its details from the ListView.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-find-item',
template: `
<button (click)="findItemById('2')">Find Item 2</button>
<ejs-listview
#findlist
id='find-list'
[dataSource]='items'>
</ejs-listview>
<div *ngIf="foundItem">
<h4>Found Item:</h4>
<p>{{ foundItem.text }}</p>
<p>ID: {{ foundItem.id }}</p>
</div>
`
})
export class FindItemComponent {
@ViewChild('findlist') findlistInstance?: ListViewComponent;
public items = [
{ text: 'React', id: '1' },
{ text: 'Vue', id: '2' },
{ text: 'Svelte', id: '3' }
];
public foundItem: any = null;
findItemById(id: string) {
if (!this.findlistInstance) return;
const element = this.findlistInstance.element.querySelector(
`[data-uid="${id}"]`
);
if (element) {
this.foundItem = this.findlistInstance.findItem(element);
console.log('Found:', this.foundItem);
}
}
}---
hideItem(item) & showItem(item) Methods
Parameter: item - Fields | HTMLElement | Element Returns: void
Hides or shows specific list items without removing them from data.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-hide-items',
template: `
<button (click)="toggleVisibility('2')">Toggle Item 2</button>
<ejs-listview
#hidelist
id='hide-list'
[dataSource]='items'>
</ejs-listview>
`
})
export class HideItemsComponent {
@ViewChild('hidelist') hidelistInstance?: ListViewComponent;
public items = [
{ text: 'Visible Item 1', id: '1' },
{ text: 'Item to Hide', id: '2' },
{ text: 'Visible Item 3', id: '3' }
];
toggleVisibility(id: string) {
if (!this.hidelistInstance) return;
const element = this.hidelistInstance.element.querySelector(
`[data-uid="${id}"]`
) as HTMLElement;
if (element) {
const isHidden = element.style.display === 'none';
if (isHidden) {
this.hidelistInstance.showItem(element);
} else {
this.hidelistInstance.hideItem(element);
}
}
}
}---
refreshItemHeight() Method
Returns: void
Recalculates item heights in virtualized ListView. Call after dynamically changing item heights.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent, VirtualizationService } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
providers: [VirtualizationService],
standalone: true,
selector: 'app-refresh-height',
template: `
<button (click)="expandAll()">Expand All</button>
<ejs-listview
#virtuallist
id='virtual-list'
[dataSource]='largeData'
[enableVirtualization]='true'
[itemHeight]='40'
[height]='500'>
</ejs-listview>
`,
styles: [`
:host ::ng-deep .expanded .e-list-item {
min-height: 100px !important;
}
`]
})
export class RefreshHeightComponent {
@ViewChild('virtuallist') virtuallistInstance?: ListViewComponent;
public largeData = Array.from({ length: 5000 }, (_, i) => ({
text: `Item ${i + 1}`,
id: i.toString()
}));
expandAll() {
if (!this.virtuallistInstance) return;
// Expand all items in DOM
const items = this.virtuallistInstance.element.querySelectorAll('.e-list-item');
items.forEach(item => item.classList.add('expanded'));
// Recalculate heights for virtualization
this.virtuallistInstance.refreshItemHeight();
}
}---
selectMultipleItems(items) & unselectItem(item) Methods
Parameter: items - Fields[] | HTMLElement[] | Element[] Returns: void
Selects or deselects multiple items programmatically.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-multi-select',
template: `
<button (click)="selectMultipleIds(['1', '3'])">Select 1 & 3</button>
<button (click)="clearSelection()">Clear All</button>
<ejs-listview
#multilist
id='multi-list'
[dataSource]='items'>
</ejs-listview>
`
})
export class MultiSelectComponent {
@ViewChild('multilist') multilistInstance?: ListViewComponent;
public items = [
{ text: 'Python', id: '1' },
{ text: 'Java', id: '2' },
{ text: 'C++', id: '3' },
{ text: 'Go', id: '4' }
];
selectMultipleIds(ids: string[]) {
if (!this.multilistInstance) return;
const elements: HTMLElement[] = [];
ids.forEach(id => {
const element = this.multilistInstance?.element.querySelector(
`[data-uid="${id}"]`
) as HTMLElement;
if (element) elements.push(element);
});
if (elements.length > 0) {
this.multilistInstance.selectMultipleItems(elements);
}
}
clearSelection() {
if (!this.multilistInstance) return;
const allItems = this.multilistInstance.element.querySelectorAll('.e-active');
allItems.forEach((item: Element) => {
this.multilistInstance?.unselectItem(item);
});
}
}---
removeMultipleItems(items) Method
Parameter: items - HTMLElement[] | Element[] | Fields[] Returns: void
Removes multiple items from the ListView in one operation.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-remove-multiple',
template: `
<button (click)="deleteSelected()">Delete Selected</button>
<ejs-listview
#removelist
id='remove-list'
[dataSource]='items'
[showCheckBox]='true'
[fields]='fields'>
</ejs-listview>
`
})
export class RemoveMultipleComponent {
@ViewChild('removelist') removelistInstance?: ListViewComponent;
public items = [
{ text: 'Draft 1', id: '1', isChecked: false },
{ text: 'Draft 2', id: '2', isChecked: true },
{ text: 'Draft 3', id: '3', isChecked: true }
];
public fields = { isChecked: 'isChecked' };
deleteSelected() {
if (!this.removelistInstance) return;
// Get all checked items
const checkedItems = this.removelistInstance.element.querySelectorAll(
'.e-list-item.e-checked'
);
if (checkedItems.length > 0) {
this.removelistInstance.removeMultipleItems(
Array.from(checkedItems) as HTMLElement[]
);
}
}
}---
render() & destroy() Methods
Returns: void
Manually initializes or destroys the ListView component.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild, ViewContainerRef } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-lifecycle-list',
template: `
<button (click)="recreateList()">Recreate List</button>
<button (click)="destroyList()">Destroy List</button>
<div #listContainer>
<ejs-listview
*ngIf="isVisible"
#lifecyclelist
id='lifecycle-list'
[dataSource]='items'>
</ejs-listview>
</div>
`
})
export class LifecycleListComponent {
@ViewChild('lifecyclelist') lifecyclelistInstance?: ListViewComponent;
public isVisible = true;
public items = [
{ text: 'Mount', id: '1' },
{ text: 'Update', id: '2' },
{ text: 'Unmount', id: '3' }
];
destroyList() {
if (this.lifecyclelistInstance) {
this.lifecyclelistInstance.destroy();
this.isVisible = false;
}
}
recreateList() {
this.isVisible = true;
setTimeout(() => {
if (this.lifecyclelistInstance) {
this.lifecyclelistInstance.render();
}
}, 100);
}
}---
Events Reference
select Event - SelectEventArgs
Triggered when a list item is selected.
SelectEventArgs Properties:
| Property | Type | Description |
|---|---|---|
cancel | boolean | Set to true to prevent item selection |
data | `object \ | string[] \ |
event | `MouseEvent \ | KeyboardEvent` |
index | number | Index of the selected item |
isChecked | boolean | Whether checkbox is checked (if enabled) |
isInteracted | boolean | Whether event was user-triggered |
item | `HTMLElement \ | Element` |
name | string | Event name: 'select' |
text | string | The display text of selected item |
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { SelectEventArgs } from '@syncfusion/ej2-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-select-event',
template: `
<ejs-listview
id='select-list'
[dataSource]='items'
(select)='onSelect($event)'>
</ejs-listview>
<div *ngIf="selectedInfo">
<h4>Selection Details:</h4>
<p>Text: {{ selectedInfo.text }}</p>
<p>Index: {{ selectedInfo.index }}</p>
<p>User Interaction: {{ selectedInfo.isInteracted }}</p>
</div>
`
})
export class SelectEventComponent {
public items = [
{ text: 'Bike', id: '1' },
{ text: 'Car', id: '2' },
{ text: 'Truck', id: '3' }
];
public selectedInfo: any = null;
onSelect(args: SelectEventArgs) {
console.log('Selected Item:', args.data);
console.log('Index:', args.index);
console.log('User Interaction:', args.isInteracted);
console.log('Checkbox Checked:', args.isChecked);
// Prevent selection of specific items
if (args.data.id === '3') {
args.cancel = true; // Prevent Truck from being selected
}
this.selectedInfo = {
text: args.text,
index: args.index,
isInteracted: args.isInteracted
};
}
}---
scroll Event - ScrolledEventArgs
Triggered when scrolling reaches top or bottom of ListView.
ScrolledEventArgs Properties:
| Property | Type | Description |
|---|---|---|
distanceX | number | Horizontal scroll distance in pixels |
distanceY | number | Vertical scroll distance in pixels |
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { ScrolledEventArgs } from '@syncfusion/ej2-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-scroll-event',
template: `
<ejs-listview
id='scroll-list'
[dataSource]='items'
[height]='400'
(scroll)='onScroll($event)'>
</ejs-listview>
<p>Scroll Position: {{ scrollPosition }}px</p>
<p>{{ scrollMessage }}</p>
`
})
export class ScrollEventComponent {
public items = Array.from({ length: 100 }, (_, i) => ({
text: `Item ${i + 1}`,
id: i.toString()
}));
public scrollPosition = 0;
public scrollMessage = '';
onScroll(args: ScrolledEventArgs) {
this.scrollPosition = args.distanceY;
// Detect when near bottom for lazy loading
if (args.distanceY > 90) {
this.scrollMessage = '📥 Near bottom - ready to load more';
this.loadMoreData();
} else if (args.distanceY < 10) {
this.scrollMessage = '📤 At top';
}
}
loadMoreData() {
console.log('Loading more items...');
// Fetch and append new items
}
}---
actionBegin & actionComplete Events
Triggered before and after ListView operations (add, remove, etc.).
Event Properties:
| Property | Type | Description |
|---|---|---|
eventName | string | Name of the action: 'add', 'remove', 'select' |
data | object[] | Items involved in the action |
cancel | boolean | Set to true to cancel the action |
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-action-events',
template: `
<button (click)="addItem()">Add New</button>
<p>Status: {{ status }}</p>
<ejs-listview
#actionlist
id='action-list'
[dataSource]='items'
(actionBegin)='onActionBegin($event)'
(actionComplete)='onActionComplete($event)'>
</ejs-listview>
`
})
export class ActionEventsComponent {
@ViewChild('actionlist') actionlistInstance?: ListViewComponent;
public items: any[] = [
{ text: 'Task 1', id: '1' },
{ text: 'Task 2', id: '2' }
];
public status = 'Ready';
addItem() {
const newItem = { text: `Task ${this.items.length + 1}`, id: Date.now().toString() };
if (this.actionlistInstance) {
this.actionlistInstance.addItem([newItem]);
}
}
onActionBegin(event: any) {
console.log(`Action starting: ${event.eventName}`);
this.status = `⏳ ${event.eventName} in progress...`;
}
onActionComplete(event: any) {
console.log(`Action completed: ${event.eventName}`);
this.status = `✅ ${event.eventName} completed`;
}
}---
actionFailure Event
Triggered when a remote data fetch fails.
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { DataManager } from '@syncfusion/ej2-data';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-action-failure',
template: `
<ejs-listview
id='failure-list'
[dataSource]='dataManager'
[fields]='fields'
(actionFailure)='onActionFailure($event)'>
</ejs-listview>
<div *ngIf="error" class="error">
❌ {{ error }}
</div>
`,
styles: [`
.error {
color: #d32f2f;
padding: 12px;
border: 1px solid #d32f2f;
border-radius: 4px;
}
`]
})
export class ActionFailureComponent {
public dataManager = new DataManager({
url: 'https://invalid-domain-that-fails.com/api/data',
crossDomain: true
});
public fields = { id: 'id', text: 'name' };
public error = '';
onActionFailure(event: any) {
console.error('Data fetch failed:', event);
this.error = `Failed to load data: ${event.error?.message || 'Unknown error'}`;
}
}---
API Coverage Summary
Complete Reference:
- ✅ 9 Properties (animation, enablePersistence, enableRtl, locale, query, width/height, sortOrder, cssClass, htmlAttributes)
- ✅ 12 Methods (back, checkAll/uncheckAll, checkItem/uncheckItem, disableItem/enableItem, findItem, hideItem/showItem, refreshItemHeight, selectMultipleItems, unselectItem, removeMultipleItems, render/destroy)
- ✅ 5 Events with Complete Arguments (select, scroll, actionBegin, actionComplete, actionFailure)
All examples are production-ready and follow Angular 21+ standalone patterns.
Checklist and Item States in Syncfusion Angular ListView
Table of Contents
- Basic Checklist
- Checkbox Styling
- Item State Management
- Multi-State Checkboxes
- Checklist Patterns
- Progress Tracking
---
Basic Checklist
Simple Checklist with Checkboxes
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-basic-checklist',
template: `
<ejs-listview
id='basic-checklist'
[dataSource]='tasks'
[showCheckBox]='true'
[fields]='fields'
headerTitle='My Tasks'
[showHeader]='true'>
</ejs-listview>
`
})
export class BasicChecklistComponent {
public tasks = [
{ text: 'Buy groceries', id: '1', isChecked: false },
{ text: 'Prepare dinner', id: '2', isChecked: true },
{ text: 'Clean house', id: '3', isChecked: false },
{ text: 'Review code', id: '4', isChecked: true },
{ text: 'Write documentation', id: '5', isChecked: false }
];
public fields = {
id: 'id',
isChecked: 'isChecked'
};
}Checklist with Icons and Styling
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ListViewModule, CommonModule],
standalone: true,
selector: 'app-styled-checklist',
template: `
<ejs-listview
id='styled-checklist'
[dataSource]='tasks'
[showCheckBox]='true'
[fields]='fields'
cssClass='e-list-template'
(change)="onCheckChange($event)">
<ng-template #template let-data="">
<div class="e-list-wrapper" [class.completed]="data.isChecked">
<span class="task-icon" [ngClass]="data.icon"></span>
<span class="task-text" [class.strikethrough]="data.isChecked">
{{data.text}}
</span>
<span class="priority-badge" [ngClass]="'priority-' + data.priority">
{{data.priority}}
</span>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.completed {
opacity: 0.6;
}
.task-icon {
margin-right: 10px;
font-size: 18px;
}
.task-text {
flex: 1;
}
.strikethrough {
text-decoration: line-through;
color: #999;
}
.priority-badge {
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: bold;
color: white;
}
.priority-high {
background: #f44336;
}
.priority-medium {
background: #ff9800;
}
.priority-low {
background: #4caf50;
}
`]
})
export class StyledChecklistComponent {
public tasks = [
{ text: 'Buy groceries', id: '1', isChecked: false, icon: 'e-icons e-shopping-cart', priority: 'high' },
{ text: 'Prepare dinner', id: '2', isChecked: true, icon: 'e-icons e-utensils', priority: 'high' },
{ text: 'Clean house', id: '3', isChecked: false, icon: 'e-icons e-broom', priority: 'medium' },
{ text: 'Review code', id: '4', isChecked: true, icon: 'e-icons e-code', priority: 'high' },
{ text: 'Write docs', id: '5', isChecked: false, icon: 'e-icons e-document', priority: 'low' }
];
public fields = {
id: 'id',
isChecked: 'isChecked'
};
onCheckChange(event: any) {
console.log('Task toggled:', event);
}
}---
Checkbox Styling
Custom Checkbox Appearance
/* Style checkboxes */
.e-listview .e-list-item .e-checkbox-wrapper {
margin-right: 15px;
}
.e-listview .e-list-item .e-checkbox-wrapper input {
width: 18px;
height: 18px;
cursor: pointer;
}
.e-listview .e-list-item .e-checkbox-wrapper label {
cursor: pointer;
user-select: none;
}
/* Checked state styling */
.e-listview .e-list-item .e-checkbox-wrapper input:checked + label::before {
background: #007bff;
border-color: #007bff;
}
/* Hover state */
.e-listview .e-list-item .e-checkbox-wrapper:hover input + label::before {
border-color: #007bff;
}
/* Focus state (accessibility) */
.e-listview .e-list-item .e-checkbox-wrapper input:focus + label::before {
outline: 2px solid #007bff;
}Large Checkboxes for Accessibility
.e-listview.large-checkboxes .e-checkbox-wrapper {
transform: scale(1.5);
transform-origin: left;
}
.e-listview.large-checkboxes .e-list-item {
padding: 20px 15px;
}---
Item State Management
Managing Enabled/Disabled States
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ListViewModule, CommonModule],
standalone: true,
selector: 'app-item-states',
template: `
<ejs-listview
id='state-list'
[dataSource]='items'
[showCheckBox]='true'
[fields]='fields'
cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper" [class.disabled]="!data.enabled">
<span class="state-indicator" [ngClass]="data.state">
{{getStateIcon(data.state)}}
</span>
<span class="item-name">{{data.name}}</span>
<span class="state-label" [ngClass]="'state-' + data.state">
{{data.state | uppercase}}
</span>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.disabled {
opacity: 0.5;
pointer-events: none;
background-color: #f5f5f5;
}
.state-indicator {
margin-right: 10px;
font-size: 18px;
}
.state-indicator.active {
color: #4caf50;
}
.state-indicator.pending {
color: #ff9800;
}
.state-indicator.inactive {
color: #999;
}
.state-label {
margin-left: auto;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: bold;
color: white;
}
.state-active {
background: #4caf50;
}
.state-pending {
background: #ff9800;
}
.state-inactive {
background: #999;
}
`]
})
export class ItemStatesComponent {
public items = [
{ name: 'Active Task', id: '1', state: 'active', enabled: true, isChecked: true },
{ name: 'Pending Task', id: '2', state: 'pending', enabled: true, isChecked: false },
{ name: 'Inactive Task', id: '3', state: 'inactive', enabled: false, isChecked: false },
{ name: 'Active Task 2', id: '4', state: 'active', enabled: true, isChecked: true }
];
public fields = {
id: 'id',
isChecked: 'isChecked',
enabled: 'enabled'
};
getStateIcon(state: string): string {
const icons: { [key: string]: string } = {
'active': '✓',
'pending': '⏳',
'inactive': '○'
};
return icons[state] || '';
}
}---
Multi-State Checkboxes
Tri-State Checkbox (Indeterminate State)
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-tri-state-checkbox',
template: `
<div>
<button (click)="selectAll()">Select All</button>
<button (click)="deselectAll()">Deselect All</button>
</div>
<ejs-listview
#listview
id='tri-state-list'
[dataSource]='items'
[showCheckBox]='true'
[fields]='fields'
(change)="updateTriState()">
</ejs-listview>
<div class="state-info">
<span>Selected: {{selectedCount}}</span>
<span>Total: {{items.length}}</span>
</div>
`,
styles: [`
.state-info {
padding: 15px;
background: #f5f5f5;
display: flex;
gap: 20px;
font-size: 14px;
}
`]
})
export class TriStateCheckboxComponent {
@ViewChild('listview') listViewInstance!: ListViewComponent;
public items = [
{ text: 'Item 1', id: '1', isChecked: true },
{ text: 'Item 2', id: '2', isChecked: false },
{ text: 'Item 3', id: '3', isChecked: true },
{ text: 'Item 4', id: '4', isChecked: false }
];
public fields = { id: 'id', isChecked: 'isChecked' };
public selectedCount: number = 2;
selectAll() {
this.items.forEach(item => item.isChecked = true);
this.listViewInstance.refresh();
this.updateTriState();
}
deselectAll() {
this.items.forEach(item => item.isChecked = false);
this.listViewInstance.refresh();
this.updateTriState();
}
updateTriState() {
this.selectedCount = this.items.filter(i => i.isChecked).length;
this.updateIndeterminateState();
}
updateIndeterminateState() {
const checkboxes = document.querySelectorAll('.e-checkbox-wrapper input');
const allChecked = this.selectedCount === this.items.length;
const noneChecked = this.selectedCount === 0;
if (!allChecked && !noneChecked) {
// Set indeterminate state
checkboxes.forEach((cb: any) => {
cb.indeterminate = true;
});
} else {
checkboxes.forEach((cb: any) => {
cb.indeterminate = false;
});
}
}
}---
Checklist Patterns
Todo List with Categories
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-todo-list',
template: `
<input
type="text"
placeholder="Add new task..."
(keyup.enter)="addTask($event)"
class="task-input">
<ejs-listview
#todolist
id='todo-list'
[dataSource]='todos'
[showCheckBox]='true'
[fields]='fields'
[groupBy]='groupBy'
cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper" [class.completed]="data.isChecked">
<span class="todo-text">{{data.text}}</span>
<span class="due-date">{{data.dueDate}}</span>
<button (click)="deleteTask(data.id)" class="delete-btn">×</button>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.task-input {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.completed {
opacity: 0.6;
text-decoration: line-through;
}
.todo-text {
flex: 1;
}
.due-date {
font-size: 12px;
color: #999;
margin-right: 10px;
}
.delete-btn {
background: #f44336;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
}
`]
})
export class TodoListComponent {
@ViewChild('todolist') todoListInstance!: ListViewComponent;
public todos = [
{ text: 'Buy milk', id: '1', isChecked: false, dueDate: 'Today', category: 'Shopping' },
{ text: 'Complete project', id: '2', isChecked: true, dueDate: 'Tomorrow', category: 'Work' },
{ text: 'Call Mom', id: '3', isChecked: false, dueDate: 'This Week', category: 'Personal' },
{ text: 'Finish report', id: '4', isChecked: false, dueDate: 'Friday', category: 'Work' }
];
public fields = {
id: 'id',
isChecked: 'isChecked',
groupBy: 'category'
};
public groupBy: string = 'category';
addTask(event: any) {
const input = event.target as HTMLInputElement;
if (input.value.trim()) {
const newTask = {
text: input.value,
id: Date.now().toString(),
isChecked: false,
dueDate: 'Today',
category: 'General'
};
this.todos.push(newTask);
this.todoListInstance.refresh();
input.value = '';
}
}
deleteTask(taskId: string) {
this.todos = this.todos.filter(t => t.id !== taskId);
this.todoListInstance.refresh();
}
}---
Progress Tracking
Visual Progress Indicator
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-progress-checklist',
template: `
<div class="progress-container">
<div class="progress-label">
Progress: {{completedCount}}/{{tasks.length}} completed
</div>
<div class="progress-bar">
<div class="progress-fill" [style.width]="progressPercent + '%'"></div>
</div>
</div>
<ejs-listview
#tasklist
id='progress-list'
[dataSource]='tasks'
[showCheckBox]='true'
[fields]='fields'
(change)="updateProgress()">
</ejs-listview>
`,
styles: [`
.progress-container {
margin-bottom: 20px;
}
.progress-label {
font-size: 14px;
margin-bottom: 8px;
font-weight: 600;
}
.progress-bar {
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4caf50, #45a049);
transition: width 0.3s ease;
}
`]
})
export class ProgressChecklistComponent {
@ViewChild('tasklist') taskListInstance!: ListViewComponent;
public tasks = [
{ text: 'Task 1', id: '1', isChecked: true },
{ text: 'Task 2', id: '2', isChecked: true },
{ text: 'Task 3', id: '3', isChecked: false },
{ text: 'Task 4', id: '4', isChecked: false },
{ text: 'Task 5', id: '5', isChecked: false }
];
public fields = { id: 'id', isChecked: 'isChecked' };
public completedCount: number = 2;
public progressPercent: number = 40;
updateProgress() {
this.completedCount = this.tasks.filter(t => t.isChecked).length;
this.progressPercent = (this.completedCount / this.tasks.length) * 100;
}
}---
Best Practices
✅ Provide clear visual feedback for checked/unchecked states ✅ Use appropriate colors for different states ✅ Make checkboxes large enough for easy clicking ✅ Include progress indicators for multi-step checklists ✅ Support keyboard navigation (Space to toggle) ✅ Persist checklist state to storage (localStorage, database) ✅ Group related items for better organization ✅ Provide undo functionality for important actions ✅ Test on mobile devices for touch usability ✅ Use meaningful labels for checkbox items ✅ Hide checkboxes for leaf nodes in nested lists when appropriate
---
Hide Checkboxes for Specific Items
The checkbox visibility can be controlled per item using the htmlAttributes property with a custom CSS class.
Basic Hide Checkbox Example
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-mixed-checkbox',
template: `
<ejs-listview
#list
id='mixed-checkbox-list'
[dataSource]='dataSource'
[showCheckBox]='true'
[fields]='fields'
[sortOrder]='Ascending'
headerTitle='Mixed Checkbox List'
[showHeader]='true'
(select)="onSelect($event)">
</ejs-listview>
`,
styles: [`
/* Hide checkbox for items with e-checkbox-hidden class */
:host ::ng-deep .e-checkbox-hidden .e-checkbox-wrapper {
display: none;
}
/* Keep item selectable but without visible checkbox */
:host ::ng-deep .e-checkbox-hidden {
padding-left: 8px;
}
`]
})
export class MixedCheckboxComponent {
@ViewChild('list') listViewInstance?: ListViewComponent;
public dataSource: any[] = [
{
'text': 'Asia',
'id': '01',
'child': [
{
'text': 'India',
'id': '1',
'child': [
{
'text': 'Delhi',
'id': '1001',
'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' }
},
{
'text': 'Kashmir',
'id': '1002',
'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' }
},
{
'text': 'Goa',
'id': '1003',
'htmlAttributes': { 'class': 'e-file' }
}
]
},
{
'text': 'China',
'id': '2',
'child': [
{
'text': 'Zhejiang',
'id': '2001',
'htmlAttributes': { 'class': 'e-file' }
},
{
'text': 'Hunan',
'id': '2002',
'htmlAttributes': { 'class': 'e-file e-checkbox-hidden' }
}
]
}
]
}
];
public fields = {
tooltip: 'text',
id: 'id'
};
Ascending: any;
onSelect(args: any) {
// Handle selection for items with hidden checkboxes
if (this.listViewInstance && this.listViewInstance.element) {
const hiddenCheckboxElements =
this.listViewInstance.element.querySelectorAll('.e-checkbox-hidden');
// Remove 'e-active' class from all hidden-checkbox elements
hiddenCheckboxElements.forEach((element: Element) => {
(element as HTMLElement).classList.remove('e-active');
});
// Add 'e-active' class to currently selected item if it has hidden checkbox
if (args.item && args.item.classList.contains('e-checkbox-hidden')) {
args.item.classList.add('e-active');
}
}
}
}Use Cases for Hidden Checkboxes
Scenario: Leaf nodes only in nested list
- Parent items (folders/categories) can be checked
- Leaf items (files/specific items) hide checkboxes
- User manually selects what they need without checkbox UI
Scenario: Mixed selection interface
- Some items are always selectable (no checkbox needed)
- Other items are multi-select with visible checkboxes
- Reduces visual complexity in mixed-purpose lists
Styling Hidden Checkbox Items
/* Align items with hidden checkboxes properly */
.e-checkbox-hidden {
margin-left: 0;
padding-left: 8px;
}
/* Alternative: show a different icon instead of checkbox */
.e-checkbox-hidden::before {
content: "●"; /* Bullet point instead of checkbox */
margin-right: 8px;
color: #999;
}
/* Highlight hidden-checkbox items differently */
.e-checkbox-hidden {
background-color: #f9f9f9;
color: #666;
}
.e-checkbox-hidden:hover {
background-color: #f0f0f0;
}Customization and Templates in Syncfusion Angular ListView
Table of Contents
- Built-in CSS Classes
- Header Template
- Item Template
- Group Template
- Dynamic Templates
- Advanced Template Patterns
---
Built-in CSS Classes
The ListView provides predefined CSS classes to structure templates consistently:
| CSS Class | Purpose | Example Usage |
|---|---|---|
e-list-template | Mark ListView as template-enabled | Add to <ejs-listview> |
e-list-wrapper | Container for template content | Wrap each list item |
e-list-content | Align list content vertically | Wrap text content |
e-list-avatar | Enable avatar styling | Use with e-avatar class |
e-list-avatar-right | Avatar on right side | Align avatar right |
e-list-badge | Enable badge styling | Use with e-badge class |
e-list-multi-line | Multi-line item layout | Header + description |
e-list-item-header | Item header text | Use with multi-line |
Structure example:
<div class="e-list-wrapper e-list-multi-line e-list-avatar">
<span class="e-avatar e-avatar-circle">Avatar</span>
<span class="e-list-item-header">Header</span>
<span class="e-list-content">Content</span>
</div>---
Header Template
Customize the ListView header with buttons, search bars, or custom content.
Basic Header with Buttons
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule, ButtonModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='fruits-list'
[dataSource]='data'
[showHeader]='true'>
<ng-template #headerTemplate>
<div class="header-container">
<span class="header-title">Fruits</span>
<button ejs-button
iconCss='e-icons e-search-icon'
cssClass='e-small e-round'
isPrimary='true'>
</button>
<button ejs-button
iconCss='e-icons e-add-icon'
cssClass='e-small e-round'
isPrimary='true'>
</button>
<button ejs-button
iconCss='e-icons e-sort-icon'
cssClass='e-small e-round'
isPrimary='true'>
</button>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
}
.header-title {
font-weight: bold;
font-size: 16px;
}
`]
})
export class AppComponent {
public data = [
{ text: 'Date', id: '1' },
{ text: 'Fig', id: '2' },
{ text: 'Apple', id: '3' },
{ text: 'Apricot', id: '4' }
];
}Header with Search Bar
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='search-list'
[dataSource]='data'
[showHeader]='true'>
<ng-template #headerTemplate>
<div class="search-header">
<input
type="text"
placeholder="Search items..."
(input)="onSearch($event)"
class="search-input">
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.search-header {
padding: 10px;
}
.search-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
`]
})
export class AppComponent {
public data = [
{ text: 'Apple', id: '1' },
{ text: 'Apricot', id: '2' },
{ text: 'Banana', id: '3' }
];
onSearch(event: any) {
const searchValue = event.target.value.toLowerCase();
// Filter logic here
}
}---
Item Template
Customize individual list items with complex layouts using ng-template.
Multi-line Item Template with Avatar
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ListViewModule, CommonModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='contacts-list'
[dataSource]='contacts'
cssClass='e-list-template'
[headerTitle]='headerTitle'
[showHeader]='true'>
<ng-template #template let-data="">
<div class="e-list-wrapper e-list-multi-line e-list-avatar">
<!-- Avatar with initials or image -->
<span
class="e-avatar e-avatar-circle"
*ngIf="data.avatar">
{{data.avatar}}
</span>
<img
class="e-avatar e-avatar-circle"
*ngIf="data.imageUrl"
[src]="data.imageUrl">
<!-- Header and content -->
<span class="e-list-item-header">{{data.name}}</span>
<span class="e-list-content">{{data.email}}</span>
</div>
</ng-template>
</ejs-listview>
`
})
export class AppComponent {
public contacts = [
{
name: 'Jennifer',
email: '(206) 555-9854',
id: '1',
avatar: 'J',
imageUrl: ''
},
{
name: 'Amanda',
email: '(206) 555-3412',
id: '2',
avatar: 'A',
imageUrl: ''
},
{
name: 'Isabella',
email: '(206) 555-8122',
id: '3',
avatar: '',
imageUrl: 'https://example.com/isabella.jpg'
}
];
public headerTitle: string = 'Contacts';
}Item Template with Badges
<ejs-listview
id='products-list'
[dataSource]='products'
cssClass='e-list-template'>
<ng-template #template let-data="">
<div class="e-list-wrapper e-list-badge">
<span class="e-list-content">{{data.name}}</span>
<span class="e-badge e-badge-primary">{{data.stock}}</span>
</div>
</ng-template>
</ejs-listview>Complex Item Template with Conditional Content
<ng-template #template let-data="">
<div class="e-list-wrapper e-list-multi-line">
<!-- Show status indicator -->
<div class="status-indicator"
[ngClass]="data.status === 'active' ? 'active' : 'inactive'">
</div>
<!-- Main content -->
<span class="e-list-item-header">{{data.title}}</span>
<span class="e-list-content">{{data.description}}</span>
<!-- Meta information -->
<span class="e-list-content meta">{{data.date}}</span>
</div>
</ng-template>---
Group Template
Customize group headers when items are grouped by a category field.
Basic Group Template with Item Count
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='grouped-list'
[dataSource]='employees'
cssClass='e-list-template'
[fields]='fields'>
<!-- Item template -->
<ng-template #template let-data="">
<div class="e-list-wrapper e-list-multi-line e-list-avatar">
<span class="e-avatar e-avatar-circle">{{data.avatar}}</span>
<span class="e-list-item-header">{{data.name}}</span>
<span class="e-list-content">{{data.email}}</span>
</div>
</ng-template>
<!-- Group template -->
<ng-template #groupTemplate let-data="">
<div class="group-header">
<span class="group-title">{{data.items[0].department}}</span>
<span class="item-count">{{data.items.length}} employees</span>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.group-header {
display: flex;
justify-content: space-between;
padding: 12px;
background: #f5f5f5;
font-weight: bold;
}
.group-title {
color: #333;
}
.item-count {
color: #999;
font-size: 12px;
}
`]
})
export class AppComponent {
public employees = [
{ name: 'Nancy', email: 'nancy@example.com', department: 'Sales', avatar: 'N', id: '1' },
{ name: 'Andrew', email: 'andrew@example.com', department: 'Sales', avatar: 'A', id: '2' },
{ name: 'Janet', email: 'janet@example.com', department: 'IT', avatar: 'J', id: '3' },
{ name: 'Steven', email: 'steven@example.com', department: 'IT', avatar: 'S', id: '4' }
];
public fields = {
text: 'name',
id: 'id',
groupBy: 'department'
};
}Advanced Group Template with Statistics
<ng-template #groupTemplate let-data="">
<div class="advanced-group-header">
<span class="group-icon">{{getGroupIcon(data.items[0].department)}}</span>
<div class="group-info">
<span class="group-title">{{data.items[0].department}}</span>
<span class="group-stats">{{data.items.length}} items • Active: {{getActiveCount(data.items)}}</span>
</div>
<span class="expand-icon">›</span>
</div>
</ng-template>---
Dynamic Templates
Template Based on Device Size
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
imports: [ListViewModule, CommonModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='responsive-list'
[dataSource]='data'
cssClass='e-list-template'>
<!-- Desktop template -->
<ng-template #template let-data="" *ngIf="isMobile === false">
<div class="e-list-wrapper desktop-item">
<img [src]="data.image" alt="{{data.name}}" class="item-image">
<div class="item-details">
<span class="item-title">{{data.name}}</span>
<span class="item-desc">{{data.description}}</span>
<span class="item-price">\${{data.price}}</span>
</div>
</div>
</ng-template>
<!-- Mobile template -->
<ng-template #template let-data="" *ngIf="isMobile === true">
<div class="e-list-wrapper mobile-item">
<span class="item-title">{{data.name}}</span>
<span class="item-price">\${{data.price}}</span>
</div>
</ng-template>
</ejs-listview>
`,
styles: [`
.desktop-item {
display: flex;
gap: 15px;
padding: 15px;
}
.item-image {
width: 80px;
height: 80px;
border-radius: 4px;
}
.item-details {
flex: 1;
}
.mobile-item {
padding: 10px;
}
`]
})
export class AppComponent {
public isMobile: boolean = window.innerWidth < 768;
public data = [
{
name: 'Product 1',
description: 'High quality product',
price: 99.99,
image: 'product1.jpg',
id: '1'
}
];
constructor() {
window.addEventListener('resize', () => {
this.isMobile = window.innerWidth < 768;
});
}
}Conditional Content in Template
<ng-template #template let-data="">
<div class="e-list-wrapper">
<!-- Show different content based on data -->
<div *ngIf="data.type === 'image'" class="image-item">
<img [src]="data.url" alt="image">
</div>
<div *ngIf="data.type === 'text'" class="text-item">
{{data.content}}
</div>
<div *ngIf="data.type === 'video'" class="video-item">
<video [src]="data.url" controls></video>
</div>
</div>
</ng-template>---
Advanced Template Patterns
Template with Event Handlers
<ng-template #template let-data="">
<div class="e-list-wrapper">
<span class="item-name">{{data.name}}</span>
<button (click)="editItem(data)" class="edit-btn">Edit</button>
<button (click)="deleteItem(data)" class="delete-btn">Delete</button>
</div>
</ng-template>editItem(data: any) {
console.log('Edit:', data);
}
deleteItem(data: any) {
console.log('Delete:', data);
}Nested Component in Template
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { CustomItemComponent } from './custom-item.component';
@Component({
imports: [ListViewModule, CustomItemComponent],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='data'
cssClass='e-list-template'>
<ng-template #template let-data="">
<app-custom-item [item]="data"></app-custom-item>
</ng-template>
</ejs-listview>
`
})
export class AppComponent {
public data = [{...}];
}Template with ngFor Loop
<ng-template #template let-data="">
<div class="e-list-wrapper">
<span class="item-title">{{data.name}}</span>
<div class="tags">
<span class="tag" *ngFor="let tag of data.tags">{{tag}}</span>
</div>
</div>
</ng-template>---
Checkbox Position
The ListView checkbox can be positioned on the left (default) or right side of the list item using the checkBoxPosition property.
Default: Checkbox on Left
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='sample-list'
[dataSource]='data'
[showCheckBox]='true'
checkBoxPosition='Left'>
</ejs-listview>
`
})
export class AppComponent {
public data: string[] = [
'Badminton',
'Basketball',
'Cricket',
'Golf',
'Hockey'
];
}Checkbox on Right
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='sample-list'
[dataSource]='data'
[showCheckBox]='true'
checkBoxPosition='Right'>
</ejs-listview>
`
})
export class AppComponent {
public data: string[] = [
'Badminton',
'Basketball',
'Cricket',
'Golf',
'Hockey'
];
}Available positions:
Left- Default position (left side of item text)Right- Right side of item text
Checkbox Position with Custom Template
<ejs-listview
id='checklist'
[dataSource]='data'
[showCheckBox]='true'
checkBoxPosition='Right'
[fields]='fields'
[template]='itemTemplate'>
<ng-template #itemTemplate let-data>
<div class="list-item">
<span class="item-icon">📝</span>
<span class="item-text">{{data.text}}</span>
<!-- Checkbox positioned via CSS when checkBoxPosition='Right' -->
</div>
</ng-template>
</ejs-listview>---
Performance Considerations
✅ Use simple templates when possible for better scrolling performance ✅ Avoid complex calculations inside templates ✅ Use `OnPush` change detection for large lists ✅ Leverage virtualization with enableVirtualization for large datasets ✅ Cache template expressions if computationally expensive ✅ Test template rendering with 1000+ items
---
Best Practices
✅ Always add e-list-wrapper to template root ✅ Use semantic CSS classes for consistency ✅ Keep templates responsive for mobile devices ✅ Test templates with various data states (empty, null, long text) ✅ Provide meaningful alt text for images ✅ Use CSS variables for theming consistency ✅ Document custom CSS classes in your code ✅ Match checkbox position to your design (Left for RTL, Right for LTR emphasis)
Data Binding in Syncfusion Angular ListView
Table of Contents
---
Field Configuration
The fields property maps your data object properties to ListView features. Below are all available field mappings:
| Field | Type | Description |
|---|---|---|
text | string | Primary display text (required) |
id | string | Unique identifier for list items |
isChecked | string | Boolean field for checkbox state |
isVisible | string | Boolean field for visibility (hidden items still in DOM) |
enabled | string | Boolean field for item enabled/disabled state |
iconCss | string | CSS class for icons before item text |
child | string | Field containing child data for nested lists |
tooltip | string | Text displayed on item hover |
groupBy | string | Field used for grouping items by category |
sortBy | string | Field used for sorting |
htmlAttributes | string | HTML attributes as key-value pairs |
Example field configuration:
public fields: Object = {
text: 'productName',
id: 'productId',
iconCss: 'icon',
groupBy: 'category',
isChecked: 'selected',
tooltip: 'description'
};---
Local Data Binding
Simple Array of Strings
Use when displaying simple text items (tags, categories, menus):
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `<ejs-listview [dataSource]='data'></ejs-listview>`
})
export class AppComponent {
public data: string[] = [
'Artwork',
'Abstract',
'Modern Painting',
'Ceramics',
'Animation Art',
'Oil Painting'
];
}Use when:
- Creating simple lists (tags, labels, categories)
- No additional properties needed per item
- Display value equals data value
---
Array of JSON Objects
Use when displaying complex data with multiple properties:
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='data'
[fields]='fields'
[showHeader]='true'
[headerTitle]='headerTitle'>
</ejs-listview>
`
})
export class AppComponent {
public data: Object[] = [
{ 'Name': 'Display', 'id': 'list-01' },
{ 'Name': 'Notification', 'id': 'list-02' },
{ 'Name': 'Sound', 'id': 'list-03' },
{ 'Name': 'Apps', 'id': 'list-04' },
{ 'Name': 'Storage', 'id': 'list-05' },
{ 'Name': 'Battery', 'id': 'list-06' }
];
public fields: Object = {
text: 'Name', // Display the 'Name' property
id: 'id', // Use 'id' as unique identifier
tooltip: 'Name' // Show 'Name' on hover
};
public headerTitle: string = 'Device Settings';
}Use when:
- Each item has multiple properties
- Need unique IDs for selection/events
- Want to display select properties in template
---
JSON with Nested Properties and Multiple Fields
Use for complex data structures with grouping and icons:
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='employeeData'
[fields]='fields'
[showHeader]='true'
[headerTitle]='headerTitle'>
</ejs-listview>
`
})
export class AppComponent {
public employeeData: Object[] = [
{
'EmployeeID': 1,
'FirstName': 'Nancy',
'LastName': 'Davolio',
'Department': 'Sales',
'icon': 'e-icons e-people'
},
{
'EmployeeID': 2,
'FirstName': 'Andrew',
'LastName': 'Fuller',
'Department': 'Sales',
'icon': 'e-icons e-people'
},
{
'EmployeeID': 3,
'FirstName': 'Janet',
'LastName': 'Leverling',
'Department': 'IT',
'icon': 'e-icons e-people'
}
];
// Map multiple properties for display and interaction
public fields: Object = {
text: 'FirstName', // Display first name
id: 'EmployeeID', // Use employee ID
iconCss: 'icon', // Add icon
groupBy: 'Department', // Group by department
tooltip: 'FirstName' // Show tooltip on hover
};
public headerTitle: string = 'Employees';
}Key mapping tips:
- Use consistent property names across data items
- Always map
idfield for proper identification - Map
iconCssto display related icons - Use
groupByto organize items by category
---
Remote Data Binding
Using DataManager with OData v4 API
Use when fetching data from a remote server:
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { DataManager, Query, ODataV4Adaptor } from '@syncfusion/ej2-data';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='remoteData'
[query]='query'
[fields]='fields'
[showHeader]='true'
[headerTitle]='headerTitle'>
</ejs-listview>
`
})
export class AppComponent {
// DataManager configured for OData v4 service
public remoteData: Object = new DataManager({
url: 'https://services.syncfusion.com/angular/production/api/',
crossDomain: true,
adaptor: new ODataV4Adaptor()
});
// Query to fetch specific columns and limit results
public query: Query = new Query()
.from('ListView')
.select('EmployeeID,FirstName')
.take(10);
public fields: Object = {
id: 'EmployeeID',
text: 'FirstName'
};
public headerTitle: string = 'Remote Employees';
}When to use:
- Data is too large to load locally
- Data changes frequently on server
- Need to paginate or filter large datasets
- Building enterprise applications
DataManager Adaptors:
ODataV4Adaptor- OData v4 servicesODataAdaptor- OData v2 servicesWebApiAdaptor- ASP.NET Web APIUrlAdaptor- Generic HTTP endpoints
---
Fetching from REST API
Simple REST API integration:
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
import { DataManager, UrlAdaptor, Query } from '@syncfusion/ej2-data';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
[dataSource]='restApiData'
[fields]='fields'>
</ejs-listview>
`
})
export class AppComponent {
// DataManager for REST API endpoint
public restApiData: DataManager = new DataManager({
url: 'https://api.example.com/products',
adaptor: new UrlAdaptor(),
crossDomain: true
});
public fields: Object = {
id: 'product_id',
text: 'product_name'
};
}---
Dynamic Data Updates
Adding Items Dynamically
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<button (click)="addNewItem()">Add Item</button>
<ejs-listview
#listview
[dataSource]='data'>
</ejs-listview>
`
})
export class AppComponent {
@ViewChild('listview')
listViewInstance!: ListViewComponent;
public data: Object[] = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' }
];
addNewItem() {
const newItem = {
text: 'New Item - ' + Date.now(),
id: Date.now().toString()
};
this.listViewInstance.addItem([newItem]);
}
}---
Removing Items Dynamically
removeItem(element: HTMLElement) {
this.listViewInstance.removeItem(element);
}---
Updating Entire Data Source
updateDataSource() {
this.data = [
{ text: 'Updated Item 1', id: '1' },
{ text: 'Updated Item 2', id: '2' },
{ text: 'Updated Item 3', id: '3' }
];
// Refresh ListView to reflect changes
this.listViewInstance.refresh();
}---
Field Mapping Best Practices
✅ Always map the `id` field for proper item identification ✅ Keep property names consistent across all data items ✅ Use meaningful field names (not 'a', 'b', 'c') ✅ Map `tooltip` field for better UX ✅ Use `groupBy` for organized data presentation ✅ Test with empty data to verify default behavior ✅ Validate remote data before binding ✅ Use `Query` to optimize remote data fetching
---
Common Patterns
Pattern 1: Local + Remote Toggle
useRemote: boolean = false;
get dataSource() {
return this.useRemote ? this.remoteData : this.localData;
}
toggleDataSource() {
this.useRemote = !this.useRemote;
}Pattern 2: Data Refresh on Interval
constructor() {
setInterval(() => this.refreshData(), 30000);
}
refreshData() {
this.listViewInstance.refresh();
}Pattern 3: Filter and Display
filterByCategory(category: string) {
this.data = this.allData.filter(
item => item.category === category
);
}Getting Started with Syncfusion Angular ListView
Table of Contents
- Setup Angular Environment
- Create a New Application
- Using Angular Schematics (Quick Setup)
- Installing ListView Package
- Adding CSS Styles
- Creating Your First ListView
- Running the Application
---
Setup Angular Environment
Install Angular CLI globally:
npm install -g @angular/cliTo install a specific version of Angular CLI:
npm install -g @angular/cli@21.0.0Angular 21 Standalone Architecture: Standalone components are the default in Angular 21+, providing a simpler development model without the need for NgModule declarations. This guide uses the modern standalone architecture.
---
Create a New Application
Generate a new Angular application:
ng new syncfusion-angular-appThis command will prompt you to configure settings:
- Stylesheet format: Choose CSS, SCSS, LESS, or Sass
- Routing: Enable if you need routing
- Server-side rendering (SSR): Configure as needed
- AI tools: Select optional AI tooling
Example with SCSS styling:
ng new syncfusion-angular-app --style=scssNavigate to your project:
cd syncfusion-angular-appNote: Angular 20+ uses simplified file names (app.ts,app.html,app.css) without the.component.suffix, while Angular 19 and below useapp.component.ts,app.component.html, etc.
---
Using Angular Schematics (Quick Setup)
Angular schematics automate the setup process, handling dependency installation, module injection, and style imports in a single command.
Requirement: Angular CLI v6 or later (check version: ng --version)Quick Start with Schematics
Step 1: Add ListView package with schematics:
ng add @syncfusion/ej2-angular-listsThis command automatically:
- Installs the
@syncfusion/ej2-angular-listspackage - Updates
package.jsonwith dependencies - Imports
ListViewModuleinto your module/standalone configuration - Adds required CSS theme imports
Step 2: Generate a pre-configured ListView component:
Generate a default ListView:
ng generate @syncfusion/ej2-angular-lists:listview-default --name=my-listviewGenerate specific features:
| Feature | Command |
|---|---|
| Checklist | ng generate @syncfusion/ej2-angular-lists:listview-checklist --name=my-checklist |
| Nested List | ng generate @syncfusion/ej2-angular-lists:listview-nestedlist --name=my-nested-list |
| Remote Data | ng generate @syncfusion/ej2-angular-lists:listview-remotelist --name=my-remote-list |
| Templates | ng generate @syncfusion/ej2-angular-lists:listview-template --name=my-template-list |
| Virtualization | ng generate @syncfusion/ej2-angular-lists:listview-virtualization --name=my-virtual-list |
Step 3: Run your application:
ng serve --openBenefits of Schematics: ✅ Zero manual configuration ✅ Automatic dependency resolution ✅ Pre-configured components with best practices ✅ Consistent project structure ✅ Reduced setup time from 10+ minutes to < 1 minute
---
Installing ListView Package
Ivy Library Distribution (Recommended for Angular 12+)
Install the Syncfusion ListView package:
npm install @syncfusion/ej2-angular-lists --saveThis package includes:
- ListView component with all features
- Supporting components and utilities
- Type definitions for TypeScript
Legacy ngcc Package (For Angular Below 12)
For older Angular versions, install the ngcc package:
npm install @syncfusion/ej2-angular-lists@ngcc --saveUpdate package.json manually if needed:
{
"dependencies": {
"@syncfusion/ej2-angular-lists": "20.2.38-ngcc"
}
}The ngcc package supports older Angular compilation pipelines. If installing without explicit tag, the Ivy package will be installed by default.
---
Adding CSS Styles
Add ListView component styles to `src/styles.css` or `styles.scss`:
/* Import base theme */
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
/* Import ListView theme */
@import "../node_modules/@syncfusion/ej2-angular-lists/styles/material3.css";Available theme options:
material3.css- Material Design 3 (recommended)bootstrap5.css- Bootstrap 5 themefabric.css- Microsoft Fabric themetailwind.css- Tailwind CSS theme
For CheckList functionality, also add Button styles:
@import "../node_modules/@syncfusion/ej2-angular-buttons/styles/material3.css";Alternative: Use CRG (Syncfusion Custom Resource Generator) to generate combined component styles.
---
Creating Your First ListView
Basic ListView with String Data
Modify `src/app/app.component.ts` (or `app.ts` for Angular 20+):
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='sample-list'
[dataSource]='data'>
</ejs-listview>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
// Simple array of strings
public data: string[] = [
'Artwork',
'Abstract',
'Modern Painting',
'Ceramics',
'Animation Art',
'Oil Painting'
];
}ListView with Object Data and Field Mapping
When working with complex objects:
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='device-list'
[dataSource]='data'
[fields]='fields'
[showHeader]='true'
[headerTitle]='headerTitle'>
</ejs-listview>
`
})
export class AppComponent {
public data: Object[] = [
{ 'Name': 'Display', 'id': 'list-01' },
{ 'Name': 'Notification', 'id': 'list-02' },
{ 'Name': 'Sound', 'id': 'list-03' },
{ 'Name': 'Apps', 'id': 'list-04' },
{ 'Name': 'Storage', 'id': 'list-05' },
{ 'Name': 'Battery', 'id': 'list-06' }
];
// Map object properties to ListView fields
public fields: Object = {
text: 'Name', // Map Name property to display text
id: 'id', // Map id for unique identification
tooltip: 'Name' // Show Name as tooltip on hover
};
public headerTitle: string = 'Device Settings';
}ListView with Headers and Custom Styling
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='fruits-list'
[dataSource]='data'
[showHeader]='true'
[headerTitle]='headerTitle'
cssClass='custom-listview'>
</ejs-listview>
`,
styles: [`
.custom-listview {
max-width: 400px;
border: 1px solid #ddd;
border-radius: 4px;
}
`]
})
export class AppComponent {
public data: string[] = [
'Date', 'Fig', 'Apple', 'Apricot', 'Grape',
'Strawberry', 'Pineapple', 'Melon', 'Lemon', 'Cherry'
];
public headerTitle: string = 'Fruits';
}---
Running the Application
Start the development server:
ng serve --openThe --open flag automatically opens the application in your default browser at http://localhost:4200.
Output will display your ListView component with the data items rendered in a list format.
---
Common Setup Issues and Solutions
Issue: Styles Not Loading
Solution: Ensure CSS imports are in the main styles.css file, not component-level CSS files.
Issue: Module Not Found Error
Solution: Verify the package is installed:
npm list @syncfusion/ej2-angular-listsIssue: Standalone Component Errors
Solution: Ensure you're importing ListViewModule in the component's imports array (not declarations).
Issue: Theme Looks Different
Solution: Make sure you're importing the correct theme CSS file matching your needs (Material3, Bootstrap5, etc.).
---
Next Steps
- Display data dynamically: See Data Binding
- Customize appearance: See Customization and Templates
- Add interactivity: See Selection and Items
- Improve performance: See Advanced Features
---
Key Takeaways
✅ Install @syncfusion/ej2-angular-lists package ✅ Import styles in main styles.css file ✅ Import ListViewModule in component's imports array ✅ Use standalone components (Angular 21+ default) ✅ Bind data via [dataSource] property ✅ Map complex objects using [fields] property
Selection and Item Management in Syncfusion Angular ListView
Table of Contents
- Selection Modes
- Getting Selected Items
- Adding Items Dynamically
- Removing Items
- Event Handling
- Programmatic Selection
---
Selection Modes
No Selection (Default)
<ejs-listview [dataSource]='data'></ejs-listview>Single Item Selection
<ejs-listview
[dataSource]='data'
(select)="onItemSelect($event)">
</ejs-listview>Multiple Selection with Checkboxes
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component } from '@angular/core';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
id='checkbox-list'
[dataSource]='items'
[showCheckBox]='true'
[fields]='fields'>
</ejs-listview>
`
})
export class AppComponent {
public items = [
{ text: 'Hennessey Venom', id: '1' },
{ text: 'Bugatti Chiron', id: '2', isChecked: true },
{ text: 'Bugatti Veyron', id: '3' },
{ text: 'SSC Ultimate Aero', id: '4', isChecked: true },
{ text: 'Koenigsegg CCR', id: '5' }
];
public fields = {
id: 'id',
isChecked: 'isChecked'
};
}Multiple Selection with Ctrl+Click
<ejs-listview
[dataSource]='data'
(select)="onMultiSelect($event)">
</ejs-listview>Users hold Ctrl (Cmd on Mac) and click to select multiple items.
---
Getting Selected Items
Using getSelectedItems() Method
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<ejs-listview
#listview
[dataSource]='data'
[showCheckBox]='true'
[fields]='fields'>
</ejs-listview>
<br/>
<button (click)="getSelected()">Get Selected Items</button>
<div id="output"></div>
`
})
export class AppComponent {
@ViewChild('listview')
listViewInstance!: ListViewComponent;
public data = [
{ text: 'Hennessey Venom', id: '1' },
{ text: 'Bugatti Chiron', id: '2', isChecked: true },
{ text: 'Bugatti Veyron', id: '3' },
{ text: 'SSC Ultimate Aero', id: '4', isChecked: true },
{ text: 'Aston Martin', id: '5' }
];
public fields = { id: 'id', isChecked: 'isChecked' };
getSelected() {
const selectedItems = this.listViewInstance.getSelectedItems();
console.log('Selected text:', selectedItems.text);
console.log('Selected data:', selectedItems.data);
console.log('Selected elements:', selectedItems.item);
// Display results
const output = document.getElementById('output');
if (output) {
output.innerHTML = `
<p>Selected: ${selectedItems.text.join(', ')}</p>
`;
}
}
}Return Values:
text: Array of selected item text valuesdata: Array of complete data objects for selected itemsitem: HTML elements of selected items
Getting Active (Selected) Item
getActiveItem() {
// Get the currently focused/selected item
const activeItem = this.listViewInstance.getSelectedItems();
if (activeItem.data && activeItem.data.length > 0) {
console.log('Active item:', activeItem.data[0]);
}
}---
Adding Items Dynamically
Adding Single Item
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<button (click)="addItem()">Add New Item</button>
<ejs-listview
#listview
[dataSource]='items'
[fields]='fields'>
</ejs-listview>
`
})
export class AppComponent {
@ViewChild('listview')
listViewInstance!: ListViewComponent;
public items: any[] = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' }
];
public fields = { text: 'text', id: 'id' };
private itemCounter = 3;
addItem() {
const newItem = {
text: `New Item ${this.itemCounter}`,
id: this.itemCounter.toString()
};
this.listViewInstance.addItem([newItem]);
this.itemCounter++;
}
}Adding Multiple Items
addMultipleItems() {
const newItems = [
{ text: 'Item A', id: 'a' },
{ text: 'Item B', id: 'b' },
{ text: 'Item C', id: 'c' }
];
this.listViewInstance.addItem(newItems);
}Adding Item at Specific Position
addItemAtIndex(data: any, index: number) {
// Get all items
const allItems = [...this.items];
// Insert at position
allItems.splice(index, 0, data);
// Update data source
this.items = allItems;
this.listViewInstance.refresh();
}Adding Item with Template
addTemplatedItem() {
const newItem = {
text: 'Contact',
email: 'contact@example.com',
id: Date.now().toString(),
avatar: 'C'
};
this.listViewInstance.addItem([newItem]);
}---
Removing Items
Remove Selected Item
removeSelectedItem() {
const selected = this.listViewInstance.getSelectedItems();
if (selected.item && selected.item.length > 0) {
this.listViewInstance.removeItem(selected.item[0]);
}
}Remove Item by Index
removeItemByIndex(index: number) {
const items = document.querySelectorAll('#listview .e-list-item');
if (items[index]) {
this.listViewInstance.removeItem(items[index] as HTMLElement);
}
}Remove Item by ID
removeItemById(itemId: string) {
const items = document.querySelectorAll('#listview .e-list-item');
items.forEach((item: any) => {
if (item.id === itemId) {
this.listViewInstance.removeItem(item);
}
});
}Remove Multiple Items
removeMultipleItems() {
const selected = this.listViewInstance.getSelectedItems();
if (selected.item && selected.item.length > 0) {
selected.item.forEach(item => {
this.listViewInstance.removeItem(item as HTMLElement);
});
}
}Clear All Items
clearAllItems() {
this.items = [];
this.listViewInstance.refresh();
}---
Event Handling
Select Event
<ejs-listview
[dataSource]='data'
(select)="onItemSelect($event)">
</ejs-listview>onItemSelect(event: any) {
console.log('Selected item:', event.data);
console.log('Item element:', event.item);
console.log('Event type:', event.type);
}ActionComplete Event (After Add/Remove)
<ejs-listview
[dataSource]='data'
(actionComplete)="onActionComplete($event)">
</ejs-listview>onActionComplete(event: any) {
console.log('Action completed:', event.eventName);
console.log('Action type:', event.type);
// Update UI or perform post-operation tasks
}Delete Event
<ejs-listview
[dataSource]='data'
(delete)="onItemDelete($event)">
</ejs-listview>onItemDelete(event: any) {
console.log('Item deleted:', event.data);
// Handle deletion side effects
}Complete Example with Events
import { ListViewModule } from '@syncfusion/ej2-angular-lists';
import { Component, ViewChild } from '@angular/core';
import { ListViewComponent } from '@syncfusion/ej2-angular-lists';
@Component({
imports: [ListViewModule],
standalone: true,
selector: 'app-root',
template: `
<div>
<button (click)="addItem()">Add Item</button>
<button (click)="removeSelected()">Remove Selected</button>
</div>
<ejs-listview
#listview
[dataSource]='items'
(select)="onSelect($event)"
(actionComplete)="onActionComplete($event)">
</ejs-listview>
<div>{{message}}</div>
`
})
export class AppComponent {
@ViewChild('listview')
listViewInstance!: ListViewComponent;
public items: any[] = [
{ text: 'Item 1', id: '1' },
{ text: 'Item 2', id: '2' }
];
public message: string = '';
onSelect(event: any) {
this.message = `Selected: ${event.data.text}`;
}
onActionComplete(event: any) {
this.message = `Action: ${event.eventName} completed`;
}
addItem() {
const newItem = {
text: `New Item ${Date.now()}`,
id: Date.now().toString()
};
this.listViewInstance.addItem([newItem]);
}
removeSelected() {
const selected = this.listViewInstance.getSelectedItems();
if (selected.item && selected.item.length > 0) {
this.listViewInstance.removeItem(selected.item[0]);
}
}
}---
Programmatic Selection
Select Item by Index
selectItemByIndex(index: number) {
const items = document.querySelectorAll('#listview .e-list-item');
if (items[index]) {
(items[index] as HTMLElement).click();
}
}Select Item by ID
selectItemById(itemId: string) {
const items = document.querySelectorAll('#listview .e-list-item');
items.forEach((item: any) => {
if (item.getAttribute('data-id') === itemId) {
item.click();
}
});
}Select All Items (with Checkbox)
selectAll() {
const checkboxes = document.querySelectorAll('.e-list-checkbox');
checkboxes.forEach((checkbox: any) => {
if (!checkbox.checked) {
checkbox.click();
}
});
}Deselect All Items
deselectAll() {
const checkboxes = document.querySelectorAll('.e-list-checkbox');
checkboxes.forEach((checkbox: any) => {
if (checkbox.checked) {
checkbox.click();
}
});
}---
Best Practices
✅ Always check if items exist before accessing ✅ Use unique IDs for all items ✅ Handle empty selection scenarios ✅ Provide feedback after add/remove operations ✅ Use checkboxes for multiple selection scenarios ✅ Validate data before adding items ✅ Update parent data source after modifications ✅ Refresh ListView after bulk operations