All Components

Sortable Overview

The Sortable provides a sortable drag-and-drop functionality to elements within a list.

Basic Usage

The following example demonstrates the Sortable in action.

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <div class="example-config">
        <h6>Items: {{items | json}}</h6>
    </div>
    <div class="container-fluid">
        <kendo-sortable
            [navigatable]="true"
            [animation] = "true"
            [data]="items"
            class="row"
            itemClass="item col-xs-6 col-sm-3"
            activeItemClass="item col-xs-6 col-sm-3 active"
        >
        </kendo-sortable>
    </div>
  `,
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['styles.css']
})
export class AppComponent {
  public items: string[] = [
    'Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7', 'Item 8'
  ];
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SortableModule } from '@progress/kendo-angular-sortable';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
})
export class AppModule {}
.item {
    background-color: #bfe7f9;
    color: #1494d0;
    border: 1px solid #fff;
    height: 70px;
    line-height: 68px;
    font-size: 16px;
    text-align: center;
    outline: none;
    cursor: move;
}

.item:hover,
.employee:hover {
    opacity: 0.8;
}

.item.active,
.employee.active {
    background-color: #27aceb;
    color: #fff;
    border-color: #27aceb;
    z-index: 10;
}

.item.disabled {
    opacity: 0.5;
    cursor: default;
}

.team {
    min-height: 240px;
    padding-top: 15px;
    padding-bottom: 15px;
    border: 1px solid #fff;
    background-color: #dff3fc;
}

.team-b {
    background-color: #fbe0e7;
}

.employee {
    background-color: #bfe7f9;
    color: #1494d0;
    margin: 1px;
    padding: 5px;
    cursor: move;
}

.team-b .employee {
    background-color: #f3b9c9;
    color: #dd4672;
}

.team-b .employee.active {
    background-color: #dd4672;
    color: #fff;
}

.empty {
    height: 150px;
}
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Installation

  1. Download and install the package:

    npm install --save @progress/kendo-angular-sortable @progress/kendo-angular-l10n @angular/animations
  2. Once installed, import the SortableModule in your application root module:

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { SortableModule } from '@progress/kendo-angular-sortable';
    import { AppComponent } from './app.component';
    
    @NgModule({
       bootstrap:    [AppComponent],
       declarations: [AppComponent],
       imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
    })
    export class AppModule {
    }
  3. You are required to install one of the Kendo UI themes for Angular to style your component. For more information on how to add the styles, refer to the article on getting started.

Dependencies

The Sortable package requires the following peer dependencies that have to be installed by your application:

  • @angular/common
  • @angular/core
  • @progress/kendo-angular-l10n
  • rxjs
  • The version of the @angular/animations module has to be exactly the same as the version of the other @angular modules that are included in your project. To sync the package versions, you might need to run npm update.
  • The Sortable package utilizes the Angular animation system, which supports a specific set of browsers.

Features

The Sortable delivers the following features:

Disabled Items

To disable items, use the disabledIndexes property.

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <div class="example-config">
        <h5>Items: {{items | json}}</h5>
        <h5>Disabled items: {{disabledIndexes}}</h5>
    </div>
    <div class="container-fluid">
        <kendo-sortable
            [data]="items"
            [navigatable]="true"
            [animation]="true"
            [disabledIndexes]="disabledIndexes"
            class="row"
            itemClass="item col-xs-6 col-sm-3"
            activeItemClass="item col-xs-6 col-sm-3 active"
            disabledItemClass="item col-xs-6 col-sm-3 disabled">
        >
        </kendo-sortable>
    </div>
  `,
  styleUrls: ['styles.css'],
  encapsulation: ViewEncapsulation.None
})
export class AppComponent {
  public disabledIndexes: number[]=[0, 2, 5, 7];
  public items: string[]=[];
  constructor() {
      for (let i=0; i < 8; i++) {
          this.items[i]="Item " + i;
      }
  }
}

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SortableModule } from '@progress/kendo-angular-sortable';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
})
export class AppModule {}
.item {
    background-color: #bfe7f9;
    color: #1494d0;
    border: 1px solid #fff;
    height: 70px;
    line-height: 68px;
    font-size: 16px;
    text-align: center;
    outline: none;
    cursor: move;
}

.item:hover,
.employee:hover {
    opacity: 0.8;
}

.item.active,
.employee.active {
    background-color: #27aceb;
    color: #fff;
    border-color: #27aceb;
    z-index: 10;
}

.item.disabled {
    opacity: 0.5;
    cursor: default;
}

.team {
    min-height: 240px;
    padding-top: 15px;
    padding-bottom: 15px;
    border: 1px solid #fff;
    background-color: #dff3fc;
}

.team-b {
    background-color: #fbe0e7;
}

.employee {
    background-color: #bfe7f9;
    color: #1494d0;
    margin: 1px;
    padding: 5px;
    cursor: move;
}

.team-b .employee {
    background-color: #f3b9c9;
    color: #dd4672;
}

.team-b .employee.active {
    background-color: #dd4672;
    color: #fff;
}

.empty {
    height: 150px;
}
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Transfer of Items

To enable the user to transfer items between different Sortables by dragging and dropping them between the lists, utilize the drag zones of the components and set the zone and acceptZones properties.

When dragged from the source to the target component, hide the currently active item in the source Sortable through the visible:false and display:none inline styles. While the dragging of the item happens, the item is removed from the source component on dragEnd (mouse up) and added to the target component on dragOver. As a result, between the dragEnd and the dragOver actions and at the same time, in both the source and the target Sortable components two identical items exist.

The following example demonstrates how to enable the dragging of items between different item lists.

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
    selector: 'my-app',
    template: `
    <div class="example-config">
            <h5>Team A: {{palettes.TeamA | json}}</h5>
            <h5>Team B: {{palettes.TeamB | json}}</h5>
    </div>
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-12 col-sm-6 team">
                <h5>Team A</h5>
                <kendo-sortable [data]="palettes.TeamA"
                    zone="twoWay"
                    emptyText="Move employees from Team B to Team A."
                    class="row"
                    itemClass="employee"
                    [emptyItemStyle]="{'min-height': '150px', width:'100%'}"
                    emptyItemClass="empty"
                    activeItemClass="employee active">
                </kendo-sortable>
            </div>
            <div class="col-xs-12 col-sm-6 team team-b">
                <h5>Team B</h5>
                <kendo-sortable [data]="palettes.TeamB"
                    zone="twoWay"
                    emptyText="Move employees from Team A to Team B."
                    class="row"
                    itemClass="employee"
                    [emptyItemStyle]="{'min-height': '150px', width:'100%'}"
                    emptyItemClass="empty"
                    activeItemClass="employee active">
                </kendo-sortable>
            </div>
        </div>
    </div>
`,
    styleUrls: ['styles.css'],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    public palettes: any=this.palettes={
        "TeamA": ['Peter Franken', 'Simon Crowther', 'Catherine Dewey'],
        "TeamB": ['Lino Rodriguez', 'Paolo Accorti']
    };
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SortableModule } from '@progress/kendo-angular-sortable';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
})
export class AppModule {}
.item {
    background-color: #bfe7f9;
    color: #1494d0;
    border: 1px solid #fff;
    height: 70px;
    line-height: 68px;
    font-size: 16px;
    text-align: center;
    outline: none;
    cursor: move;
}

.item:hover,
.employee:hover {
    opacity: 0.8;
}

.item.active,
.employee.active {
    background-color: #27aceb;
    color: #fff;
    border-color: #27aceb;
    z-index: 10;
}

.item.disabled {
    opacity: 0.5;
    cursor: default;
}

.team {
    min-height: 240px;
    padding-top: 15px;
    padding-bottom: 15px;
    border: 1px solid #fff;
    background-color: #dff3fc;
}

.team-b {
    background-color: #fbe0e7;
}

.employee {
    background-color: #bfe7f9;
    color: #1494d0;
    margin: 1px;
    padding: 5px;
    cursor: move;
}

.team-b .employee {
    background-color: #f3b9c9;
    color: #dd4672;
}

.team-b .employee.active {
    background-color: #dd4672;
    color: #fff;
}

.empty {
    height: 150px;
}
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Templates

The Sortable enables you to customize its content by using templates.

import { Component, ViewEncapsulation } from '@angular/core';
import { SortableModule } from '@progress/kendo-angular-sortable';

@Component({
  selector: 'my-app',
  styles: [`
  `],
  encapsulation: ViewEncapsulation.None,
  template: `
    <ul>
      <li>Reorder the palettes</li>
      <li>Reorder the colors in palettes</li>
      <li>Move colors between palettes</li>
    </ul>
    <kendo-sortable
      [data]="palettes"
      [itemStyle]="{'float':'left', 'display': 'inline-block', 'width': '125px', 'color': item,
                    'background-color': '#fffaed', 'margin': '4px' , 'border': '1px solid black', 'cursor': 'move'}">
        <ng-template let-palette="item">
          {{palette.name}}
          <br/><br/>
          <div style="width: 120px;">
              <kendo-sortable [data]="palette.data"
              zone="innerZone"
              [itemStyle] = "{'border': '0px', 'opacity':'1', 'cursor': 'move'}"
              [emptyItemStyle] = "{'height': '30px', 'border': '2px dashed black'}"
              [activeItemStyle] = "{'border': '2px dashed black', 'opacity':'0.7'}" >
                  <ng-template let-item="item">
                      <div [ngStyle]="{'background-color': item,'color': 'white',
                      'height': '30px', 'margin' : '2px', 'border': '1px solid black'}" >
                          {{item}}
                      </div>
                  </ng-template>
              </kendo-sortable>
           </div>
        </ng-template>
    </kendo-sortable>
  `
})
export class AppComponent {
  public colorsA: string[] = ['Violet', 'Magenta', 'Purple', 'SlateBlue'];
  public colorsB: string[] = ['SteelBlue', 'CornflowerBlue', 'RoyalBlue', 'MediumBlue'];
  public colorsC: string[] = ['LimeGreen', 'SeaGreen', 'Green', 'OliveDrab'];
  public colorsD: string[] = ['LightSalmon', 'Salmon', 'IndianRed', 'FireBrick'];

  public palettes: any[] = [
    { data: this.colorsA, name: "Palette A" },
    { data: this.colorsB, name: "Palette B" },
    { data: this.colorsC, name: "Palette C" },
    { data: this.colorsD, name: "Palette D" }
  ];
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SortableModule } from '@progress/kendo-angular-sortable';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
})
export class AppModule {}
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Events

The following example demonstrates basic Sortable events.

import { Component, ViewEncapsulation } from '@angular/core';
import { DataEvent, DragDropEvent } from '@progress/kendo-angular-sortable';

@Component({
  selector: 'my-app',
  template: `
    <div class="container-fluid" *ngFor="let group of items; let i = index">
        <kendo-sortable
            zone="default"
            [navigatable]="true"
            [animation] = "true"
            [data]="items[i]"
            class="row"
            itemClass="item col-xs-6 col-sm-3"
            activeItemClass="item col-xs-6 col-sm-3 active"
            (dataAdd)="onDataAdd(i, $event)"
            (dataRemove)="onDataRemove(i, $event)"
            (dragEnd)="onDragEnd(i, $event)"
            (dragOver)="onDragOver(i, $event)"
            (dragStart)="onDragStart(i, $event)"
        >
        </kendo-sortable>
        <event-log title="Event log" [events]="events[i]">
        </event-log>
    </div>
  `,
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['styles.css']
})
export class AppComponent {
  public items: string[][] = [
    ['Item 1', 'Item 2', 'Item 3', 'Item 4'],
    ['Item 5', 'Item 6', 'Item 7', 'Item 8']
  ];

  public events: string[][] = [[], []];

  public onDataAdd(src: number, e: DataEvent): void {
    this.log(src, 'dataAdd', e.index);
  }

  public onDataRemove(src: number, e: DataEvent): void {
    this.log(src, 'dataRemove', e.index);
  }

  public onDragEnd(src: number, e: DragDropEvent): void {
    this.log(src, 'dragEnd', e.index);
  }

  public onDragOver(src: number, e: DragDropEvent): void {
    // Not logging due to the large number of events
  }

  public onDragStart(src: number, e: DragDropEvent): void {
    this.log(src, 'dragStart', e.index);
  }

  private log(src: number, event: string, itemIndex: number): void {
    this.events[src].push(`${event} - ${this.items[src][itemIndex]}`);
  }
}

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SortableModule } from '@progress/kendo-angular-sortable';
import { AppComponent } from './app.component';
import { EventLogComponent } from './event-log.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent, EventLogComponent],
    imports:      [BrowserModule, BrowserAnimationsModule, SortableModule]
})
export class AppModule {}

import { Component, Input } from '@angular/core';

@Component({
  selector: 'event-log',
  template: `
    <div class="example-config">
      <h5>{{ title }}</h5>
      <ul class="event-log">
        <li *ngFor="let event of events.reverse()">{{ event }}</li>
      </ul>
    </div>
  `
})
export class EventLogComponent {
  @Input() title: string;
  @Input() events: string[];
}

.item {
    background-color: #bfe7f9;
    color: #1494d0;
    border: 1px solid #fff;
    height: 70px;
    line-height: 68px;
    font-size: 16px;
    text-align: center;
    outline: none;
    cursor: move;
}

.item:hover,
.employee:hover {
    opacity: 0.8;
}

.item.active,
.employee.active {
    background-color: #27aceb;
    color: #fff;
    border-color: #27aceb;
    z-index: 10;
}

.item.disabled {
    opacity: 0.5;
    cursor: default;
}

.team {
    min-height: 240px;
    padding-top: 15px;
    padding-bottom: 15px;
    border: 1px solid #fff;
    background-color: #dff3fc;
}

.team-b {
    background-color: #fbe0e7;
}

.employee {
    background-color: #bfe7f9;
    color: #1494d0;
    margin: 1px;
    padding: 5px;
    cursor: move;
}

.team-b .employee {
    background-color: #f3b9c9;
    color: #dd4672;
}

.team-b .employee.active {
    background-color: #dd4672;
    color: #fff;
}

.empty {
    height: 150px;
}
import { AppModule } from './app.module';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Known Limitations

To enable the reordering of items, you have to mark some elements as draggable in the item template—for example, button and a.

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <kendo-sortable [data]="items">
      <ng-template let-item="item">
        <button draggable="true">
          {{item}}
        </button>
      </ng-template>
    </kendo-sortable>
  `
})
export class AppComponent {
  public items: string[] = [
    'Item 1', 'Item 2', 'Item 3', 'Item 4'
  ];
}
In this article