Sortable component on github

The sortable component represents a list of items, with ability to sort them or move to another container via drag&drop. Input collection isn't mutated by the component, so events ngModelChange, onChange are using new collections.

The easiest way to add the sortable component to your app (will be added to the root module)

Basic

#

Windstorm
Bombasto
Magneta
Tornado
Mr. O
Tomato
model: [
  "Windstorm",
  "Bombasto",
  "Magneta",
  "Tornado"
]
model: [
  "Mr. O",
  "Tomato"
]
<div class="row">
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemStringsLeft"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemStringsRight"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
</div>

<div class='row'>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemStringsLeft | json }}</pre>
  </div>

  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemStringsRight | json }}</pre>
  </div>
</div>


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

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'basic-demo',
  templateUrl: './basic.component.html',
  standalone: false
})
export class DemoBasicComponent {
  itemStringsLeft = [
    'Windstorm',
    'Bombasto',
    'Magneta',
    'Tornado'
  ];

  itemStringsRight = ['Mr. O', 'Tomato'];
}

Complex data model

#

Windstorm
Bombasto
Magneta
Tornado
Mr. O
Tomato
model: [
  {
    "id": 1,
    "name": "Windstorm"
  },
  {
    "id": 2,
    "name": "Bombasto"
  },
  {
    "id": 3,
    "name": "Magneta"
  }
]
model: [
  {
    "id": 4,
    "name": "Tornado"
  },
  {
    "id": 5,
    "name": "Mr. O"
  },
  {
    "id": 6,
    "name": "Tomato"
  }
]
<div class="row">
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemObjectsLeft"
      fieldName="name"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemObjectsRight"
      fieldName="name"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
</div>

<div class='row'>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemObjectsLeft | json }}</pre>
  </div>

  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemObjectsRight | json }}</pre>
  </div>
</div>
import { Component } from '@angular/core';

interface IItemObject {
  id: number;
  name: string;
}

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'complex-datamodel-demo',
  templateUrl: './complex-datamodel.component.html',
  standalone: false
})
export class ComplexDatamodelDemoComponent {
  itemObjectsLeft: IItemObject[] = [
    { id: 1, name: 'Windstorm' },
    { id: 2, name: 'Bombasto' },
    { id: 3, name: 'Magneta' }
  ];

  itemObjectsRight: IItemObject[] = [
    { id: 4, name: 'Tornado' },
    { id: 5, name: 'Mr. O' },
    { id: 6, name: 'Tomato' }
  ];
}

Custom item template

#

0: Windstorm
1: Bombasto
2: Magneta
3: Tornado
Mr. O
Tomato
model: [
  "Mr. O",
  "Tomato"
]
model: [
  "Mr. O",
  "Tomato"
]
<ng-template #itemTemplate let-item="item" let-index="index"><span>{{index}}: {{item.value}}</span></ng-template>

<div class="row">
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemStringsLeft"
      [itemTemplate]="itemTemplate"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <bs-sortable
      [(ngModel)]="itemStringsRight"
      itemClass="sortable-item"
      itemActiveClass="sortable-item-active"
      placeholderItem="Drag here"
      placeholderClass="placeholderStyle text-center"
      wrapperClass="sortable-wrapper"
    ></bs-sortable>
  </div>
</div>

<div class='row'>
  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemStringsRight | json }}</pre>
  </div>

  <div class="col-xs-6 col-6 col-md-5 col-lg-3">
    <pre class="code-preview">model: {{ itemStringsRight | json }}</pre>
  </div>
</div>

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

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'custom-item-template-demo',
  templateUrl: './custom-item-template.html',
  standalone: false
})
export class CustomItemTemplateDemoComponent {
  itemStringsLeft: string[] = [
    'Windstorm',
    'Bombasto',
    'Magneta',
    'Tornado'
  ];

  itemStringsRight: string[] = ['Mr. O', 'Tomato'];
}

Accessibility

You can use aria-dropeffect="..." and aria-grabbed for .sortable-item. When you start drag item aria-grabbed must have true state. aria-dropeffect property is defined depending on the grabbed object.

But be careful, these attributes are deprecated.

Installation

ng add ngx-bootstrap  --component sortable
### Standalone component usage
import { SortableModule } from 'ngx-bootstrap/sortable';

@Component({
  standalone: true,
  imports: [SortableModule,...]
})
export class AppComponent(){}

### Module usage
import { SortableModule } from 'ngx-bootstrap/sortable';

@NgModule({
  imports: [SortableModule,...]
})
export class AppModule(){}

Selector

Basic

Windstorm
Bombasto
Magneta
Tornado
Mr. O
Tomato
model: [
  "Windstorm",
  "Bombasto",
  "Magneta",
  "Tornado"
]
model: [
  "Mr. O",
  "Tomato"
]

Complex data model

Windstorm
Bombasto
Magneta
Tornado
Mr. O
Tomato
model: [
  {
    "id": 1,
    "name": "Windstorm"
  },
  {
    "id": 2,
    "name": "Bombasto"
  },
  {
    "id": 3,
    "name": "Magneta"
  }
]
model: [
  {
    "id": 4,
    "name": "Tornado"
  },
  {
    "id": 5,
    "name": "Mr. O"
  },
  {
    "id": 6,
    "name": "Tomato"
  }
]

Custom item template

0: Windstorm
1: Bombasto
2: Magneta
3: Tornado
Mr. O
Tomato
model: [
  "Mr. O",
  "Tomato"
]
model: [
  "Mr. O",
  "Tomato"
]