- Notifications
You must be signed in to change notification settings - Fork159
Angular 2+ binding to SortableJS. Previously known as angular-sortablejs
License
SortableJS/ngx-sortablejs
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This package is an Angular 2+ binding forSortable.js. Supports standard arrays and AngularFormArray
.
Previously known as angular-sortablejs.
See the library in action in ademo project (the source is located insrc
directory).
Trees are also supported:tree with fake root element (*ngFor once, root can also be hidden anyway) orwithout (*ngFor 2 times).
npm i -S ngx-sortablejs sortablejsnpm i -D @types/sortablejs
You are configured now. If you use Webpack or Angular CLI go to the usage. If you have SystemJS, that's sad, but you can go to the end of the document to find configuration steps there.
First, importSortablejsModule.forRoot({ /* and here some global settings if needed */ })
into the root module of your application:
imports:[// ...SortablejsModule.forRoot({animation:150}),// ...]
Then importSortablejsModule
into the other angular modules where you want to use it:
imports:[// ...SortablejsModule,// ...]
Then usesortablejs
property on a container HTML element to tell Angular that this is a sortable container; also pass theitems
array to both*ngFor
and[sortablejs]
to register the changes automatically.
sortablejs
- directive, accepts model to be auto-updated (see examples below)sortablejsContainer
- directive input, CSS selector for the sortable container, string. Mostly required for frameworks that wrap the content into the elements where it is impossible to access the real container element (e.g. @angular/material). Example:sortablejsContainer=".mat-grid-list"
sortablejsOptions
- directive input, sortable options to pass in. Please note that in order to change the options later the whole object needs to be recreated, see belowsortablejsInit
- directive output, returns the current Sortable instance. Example:(sortablejsInit)="sortableInstance = $event"
import{Component}from'@angular/core';@Component({selector:'my-app',template:` <h2>Drag / drop the item</h2> <div [sortablejs]="items"> <div *ngFor="let item of items">{{ item }}</div> </div> `})exportclassAppComponent{items=[1,2,3,4,5];}
Pass the options withsortablejsOptions
property.
import{Component}from'@angular/core';@Component({selector:'my-app',template:` <h2>Drag / drop the item</h2> <div [sortablejs]="items" [sortablejsOptions]="{ animation: 150 }"> <div *ngFor="let item of items">{{ item }}</div> </div> `})exportclassAppComponent{items=[1,2,3,4,5];}
You can use the options'onUpdate
method to track the changes (see alsoPassing the options section):
constructor(){this.options={onUpdate:(event:any)=>{this.postChangesToServer();}};}
If you use FormArray you are able to choose a more elegant solution:
publicitems=newFormArray([newFormControl(1),newFormControl(2),newFormControl(3),]);constructor(){this.items.valueChanges.subscribe(()=>{this.postChangesToServer(this.items.value);});}
but note, that here you will be able to take the whole changed array only (no oldIndex / newIndex).
You can pass a new options object at anytime via the[sortablejsOptions]
binding and the Angular's change detection will check for the changes from the previous options and will call the low level option setter fromSortable.js to set the new option values.
Note: It will only detect changes when a brand new options object is passed, not deep changes.
The only thing which should be done is assigning thegroup
option to the both list. Everything else is handled automatically.
import{Component}from'@angular/core';import{SortablejsOptions}from'ngx-sortablejs';@Component({selector:'my-app',template:` <h2>Drag / drop the item</h2> <h3>list 1</h3> <div [sortablejs]="items1" [sortablejsOptions]="options"> <div *ngFor="let item of items1">{{ item }}</div> </div> <h3>list 2</h3> <div [sortablejs]="items2" [sortablejsOptions]="options"> <div *ngFor="let item of items2">{{ item }}</div> </div> `})exportclassAppComponent{items1=[1,2,3,4,5];items2=['a','b','c','d','e'];options:SortablejsOptions={group:'test'};}
The clone mode is similar to the one above (of course the proper Sortablejs settings should be used; see demo). The only important thing is that thengx-sortablejs
does clone the HTML element butdoes not clone the variable (orFormControl
in case ofFormArray
input). By default the variable will be taken as is: a primitive will be copied, an object will be referenced.
If you want to clone the item being sorted in a different manner, you can providesortablejsCloneFunction
as a parameter. This function receives an item and should return a clone of that item.
import{Component}from'@angular/core';import{SortablejsOptions}from'ngx-sortablejs';@Component({selector:'my-app',template:` <h2>Drag / drop the item</h2> <h3>list 1</h3> <div [sortablejs]="items1" [sortablejsOptions]="options" [sortablejsCloneFunction]="myCloneImplementation"> <div *ngFor="let item of items1">{{ item }}</div> </div> <h3>list 2</h3> <div [sortablejs]="items2" [sortablejsOptions]="options" [sortablejsCloneFunction]="myCloneImplementation"> <div *ngFor="let item of items2">{{ item }}</div> </div> `})exportclassAppComponent{myCloneImplementation=(item)=>{returnitem;// this is what happens if sortablejsCloneFunction is not provided. Add your stuff here}}
By default, the boolean parameterrunInsideAngular is set tofalse.This means that the initial binding of all mouse events of the component will be set so that theywill not trigger Angular's change detection.
If this parameter is set to true, then for large components - with a lot of data bindings - the UI will function in a staggered and lagging way (mainly when dragging items), while every event will trigger the change detection (which might be needed in some special edge cases).
If you want to use the same sortable options across different places of your application you might want to set up global configuration. Add the following to your main module to enable e.g.animation: 150
everywhere:
imports:[// ...// any properties and events available on original library work here as wellSortablejsModule.forRoot({animation:150}),// ...]
This value will be used as a default one, but it can be overwritten by a localsortablejsOptions
property.
There is a bug with expansion panel which appears because angular material does not really hide the content of panel, but usesvisibility: hidden
. What we need to do is to actually totally hide it from the DOM instead.
Just add this to yourglobal styles
mat-expansion-panel.sortable-drag .mat-expansion-panel-content {display: none;}
and the issue should be resolved.
The elements with ripple effect likemat-list-item
are affected. The dragging is broken because there is adiv created right under the cursor and the webkit has no idea what to do with it.
There are two solutions:
- Disable the ripple effect
<amat-list-item[disableRipple]="true">
- Use
handle
property and block propagation ofmousedown
andtouchstart
events on the handler to prevent ripple.
<div[sortablejs]="..."[sortablejsOptions]="{ handle: '.handle' }"><amat-list-item*ngFor="let a of b"[routerLink]="..."routerLinkActive="active"><mat-iconmatListIconclass="handle"(mousedown)="$event.stopPropagation()"(touchstart)="$event.stopPropagation()">drag_handle</mat-icon>{{a}}</a></div>
The model is automatically updated because you pass theitems
as<div [sortablejs]="items">
. Theitems
variable can be either an ordinary JavaScript array or a reactive formsFormArray
.
If you won't pass anything, e.g.<div sortablejs>
, the items won't be automatically updated, thus you should take care of updating the array on your own using standardSortable.js
events.
Original eventsonAdd
,onRemove
,onUpdate
are intercepted by the library in order to reflect the sortable changes into the data. If you will add your own event handlers (inside of the options object) they will be called right after the data binding is done. If you don't pass the data, e.g.<div sortablejs>
the data binding is skipped and only your event handlers will be fired.
Important: the originalonAdd
event happens before theonRemove
event because the original library makes it like that. We change this behavior and call 'onAdd' after the 'onRemove'. If you want to work with original onAdd event you can useonAddOriginal
which happens beforeonRemove
.
MIT
About
Angular 2+ binding to SortableJS. Previously known as angular-sortablejs
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.