{"id":13481037,"url":"https://github.com/valor-software/ng2-dragula","last_synced_at":"2025-05-13T21:08:30.297Z","repository":{"id":1207845,"uuid":"41479593","full_name":"valor-software/ng2-dragula","owner":"valor-software","description":"Simple drag and drop with dragula","archived":false,"fork":false,"pushed_at":"2024-04-15T10:51:11.000Z","size":2080,"stargazers_count":1914,"open_issues_count":98,"forks_count":434,"subscribers_count":59,"default_branch":"master","last_synced_at":"2025-05-06T03:38:44.263Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://valor-software.com/ng2-dragula/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/valor-software.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-27T10:06:18.000Z","updated_at":"2025-05-04T18:24:13.000Z","dependencies_parsed_at":"2024-01-12T18:39:32.686Z","dependency_job_id":"8cbcbf3a-8c42-43e1-be64-357bd064d3b2","html_url":"https://github.com/valor-software/ng2-dragula","commit_stats":{"total_commits":264,"total_committers":40,"mean_commits":6.6,"dds":0.6666666666666667,"last_synced_commit":"288bbdb31de06695443babf14468ecb3e382e455"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valor-software%2Fng2-dragula","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valor-software%2Fng2-dragula/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valor-software%2Fng2-dragula/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valor-software%2Fng2-dragula/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/valor-software","download_url":"https://codeload.github.com/valor-software/ng2-dragula/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253022199,"owners_count":21841951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:00:47.940Z","updated_at":"2025-05-13T21:08:25.288Z","avatar_url":"https://github.com/valor-software.png","language":"TypeScript","funding_links":[],"categories":["Uncategorized","TypeScript","Table of contents","Awesome Angular [![Awesome TipeIO](https://img.shields.io/badge/Awesome%20Angular-@TipeIO-6C6AE7.svg)](https://github.com/gdi2290/awesome-angular) [![Awesome devarchy.com](https://img.shields.io/badge/Awesome%20Angular-@devarchy.com-86BDC1.svg)](https://github.com/brillout/awesome-angular-components)"],"sub_categories":["Uncategorized","Third Party Components","Angular \u003ca id=\"angular\"\u003e\u003c/a\u003e"],"readme":"Official **Angular** wrapper for [`dragula`](https://github.com/bevacqua/dragula).\n\n[![npm version](https://badge.fury.io/js/ng2-dragula.svg)](http://badge.fury.io/js/ng2-dragula) [![npm downloads](https://img.shields.io/npm/dm/ng2-dragula.svg)](https://npmjs.org/ng2-dragula)\n[![Build Status](https://travis-ci.org/valor-software/ng2-dragula.svg?branch=master)](https://travis-ci.org/valor-software/ng2-dragula)\n[![codecov](https://codecov.io/gh/valor-software/ng2-dragula/branch/master/graph/badge.svg)](https://codecov.io/gh/valor-software/ng2-dragula)\n\n![Logo](https://raw.githubusercontent.com/bevacqua/dragula/master/resources/logo.png)\n\n\u003e Drag and drop so simple it hurts\n\n\n# Demo\n\nTry out the [demo](http://valor-software.github.io/ng2-dragula/index.html)!\n\n[//]: # (Or play with [this starter in your browser][stackblitz] on StackBlitz.)\n\n[stackblitz]: https://stackblitz.com/edit/ng2-dragula-base?file=src/app/app.component.html\n\n![Demo](https://raw.githubusercontent.com/bevacqua/dragula/master/resources/demo.png)\n\n* [Install](#install)\n* [Setup](#setup)\n* [Usage](#usage)\n  * [Directive](#directive)\n  * [Grouping containers](#grouping-containers)\n  * [Saving changes to arrays with dragulaModel](#saving-changes-to-arrays-with-dragulamodel)\n  * [DragulaService](#dragulaservice)\n  * [Drake options](#drake-options)\n  * [Events](#events)\n  * [Special events for ng2-dragula](#special-events-for-ng2-dragula)\n* [Classic Blunders](#classic-blunders)\n* [Development](#development)\n\n# Dependencies\n\nLatest version available for each version of Angular\n\n| ng2-dragula | Angular |\n| ---------- |---------|\n| 2.1.1      | \u003c= 9.x  |\n| current    | 16.x.x  |\n\n# Install\n\nYou can get it on npm.\n\n```sh\nnpm install ng2-dragula\n# or\nyarn add ng2-dragula\n```\n\n# Setup\n\n### 1. Important: add the following line to your `polyfills.ts`:\n\n```ts\n(window as any).global = window;\n```\n\nThis is a temporary workaround for\n[#849](https://github.com/valor-software/ng2-dragula/issues/849), while upstream\ndragula still relies on `global`.\n\n### 2. Add `DragulaModule.forRoot()` to your application module.\n\n```typescript\nimport { DragulaModule } from 'ng2-dragula';\n@NgModule({\n  imports: [\n    ...,\n    DragulaModule.forRoot()\n  ],\n})\nexport class AppModule { }\n```\n\nOn any child modules (like lazy loaded route modules), just use `DragulaModule`.\n\n### 3. Add the CSS to your project\n\nYou'll also need to add Dragula's CSS stylesheet `dragula.css` to your\napplication (e.g. in `styles.scss`). The following is **slightly better** than\n`node_modules/dragula/dist/dragula.css` (it includes `pointer-events: none`\n([#508](https://github.com/valor-software/ng2-dragula/issues/508)) and\n[this fix](https://github.com/bevacqua/dragula/issues/373)),\nbut you may wish to make your own modifications.\n\n```scss\n/* in-flight clone */\n.gu-mirror {\n  position: fixed !important;\n  margin: 0 !important;\n  z-index: 9999 !important;\n  opacity: 0.8;\n  -ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)\";\n  filter: alpha(opacity=80);\n  pointer-events: none;\n}\n/* high-performance display:none; helper */\n.gu-hide {\n  left: -9999px !important;\n}\n/* added to mirrorContainer (default = body) while dragging */\n.gu-unselectable {\n  -webkit-user-select: none !important;\n  -moz-user-select: none !important;\n  -ms-user-select: none !important;\n  user-select: none !important;\n}\n/* added to the source element while its mirror is dragged */\n.gu-transit {\n  opacity: 0.2;\n  -ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=20)\";\n  filter: alpha(opacity=20);\n}\n```\n\n### Then you're ready to go\n\nHere's a super quick sample to get you started:\n\n```typescript\n@Component({\n  selector: \"sample\",\n  template:`\n  \u003cdiv\u003e\n    \u003cdiv class=\"wrapper\"\u003e\n      \u003cdiv class=\"container\" dragula=\"DRAGULA_FACTS\"\u003e\n        \u003cdiv\u003eYou can move these elements between these two containers\u003c/div\u003e\n        \u003cdiv\u003eMoving them anywhere else isn't quite possible\u003c/div\u003e\n        \u003cdiv\u003eThere's also the possibility of moving elements around in the same container, changing their position\u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"container\" dragula=\"DRAGULA_FACTS\"\u003e\n        \u003cdiv\u003eThis is the default use case. You only need to specify the containers you want to use\u003c/div\u003e\n        \u003cdiv\u003eMore interactive use cases lie ahead\u003c/div\u003e\n        \u003cdiv\u003eMake sure to check out the \u003ca href=\"https://github.com/bevacqua/dragula#readme\"\u003edocumentation on GitHub!\u003c/a\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  `\n})\nclass Sample {}\n```\n\n# Usage\n\nThis package isn't very different from `dragula` itself. I'll mark the\ndifferences here, but please refer to the documentation for\n[dragula](https://github.com/bevacqua/dragula) if you need to learn more about\n`dragula` itself.\n\n## Directive\n\nThere's a `dragula` directive that makes a container's direct children\ndraggable. You must supply a string. Both syntaxes, `dragula=\"VAMPIRES\"` or\n`[dragula]=\"'VAMPIRES'\"`, work equally well.\n\n```html\n\u003cul dragula=\"VAMPIRES\"\u003e\n  \u003cli\u003eDracula\u003c/li\u003e\n  \u003cli\u003eKurz\u003c/li\u003e\n  \u003cli\u003eVladislav\u003c/li\u003e\n  \u003cli\u003eDeacon\u003c/li\u003e\n\u003c/ul\u003e\n```\n\n## Grouping containers\n\nYou can group containers together by giving them the same group name. When you\ndo, the children of each container can be dragged to any container in the same\ngroup.\n\n```html\n\u003cdiv dragula=\"VAMPIRES\"\u003e\n  \u003c!-- vamps in here --\u003e\n\u003c/div\u003e\n\u003cdiv dragula=\"VAMPIRES\"\u003e\n  \u003c!-- vamps in here --\u003e\n\u003c/div\u003e\n\n\u003cdiv dragula=\"ZOMBIES\"\u003e\n  \u003c!-- but zombies in here! --\u003e\n\u003c/div\u003e\n```\n\nIf you want to make sure you are using the same type string in different places,\nuse the `[dragula]` syntax to pass a string variable from your component:\n\n```html\n\u003cdiv [dragula]=\"Vampires\"\u003e\u003c/div\u003e\n\u003cdiv [dragula]=\"Vampires\"\u003e\u003c/div\u003e\n```\n```ts\nclass MyComponent {\n  Vampires = \"VAMPIRES\";\n}\n```\n\n## Saving changes to arrays with `[(dragulaModel)]`\n\nIf your container's children are rendered using `ngFor`, you may wish to read what your users have done. If you provide the same array to the `[(dragulaModel)]` attribute on the **container** element, any changes will be synced back to the array.\n\n**NOTE: v2 changes the behaviour of [dragulaModel]. It no longer mutates the arrays you give it, but will shallow clone them and give you the results.** Use two-way binding with `[(dragulaModel)]=\"...\"`, or use the DragulaService `dropModel` and `removeModel` events to save the new models produced.\n\n```html\n\u003cul dragula=\"VAMPIRES\" [(dragulaModel)]=\"vampires\"\u003e\n  \u003cli *ngFor=\"let vamp of vampires\"\u003e\n    {{ vamp.name }} likes {{ vamp.favouriteColor }}\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nYou do not, of course, *have* to sync the changes back. The `[(dragulaModel)]` syntax is equivalent to:\n\n```html\n\u003cul dragula=\"VAMPIRES\" [dragulaModel]=\"vampires\" (dragulaModelChange)=\"vampires = $event\"\u003e\n  ...\n\u003c/ul\u003e\n```\n\nNote: **DO NOT** put any other elements inside the container. The library relies\non having the index of a DOM element inside a container mapping directly to\ntheir associated items in the array. Everything will be messed up if you do\nthis.\n\nOn top of the normal Dragula events, when `[(dragulaModel)]` is provided, there are two extra events: `dropModel` and `removeModel`. Further details are available under `Events`\n\n## Drake options\n\nIf you need to configure the `drake` _(there's exactly one `drake` per `group`)_, you can use the `DragulaService`.\n\n```ts\nimport { DragulaService } from 'ng2-dragula';\n\nclass ConfigExample {\n  constructor(private dragulaService: DragulaService) {\n    dragulaService.createGroup(\"VAMPIRES\", {\n      removeOnSpill: true\n    });\n  }\n}\n```\n\nSee below for more info on options.\n\n## `DragulaService`\n\nThis service exposes a few different methods with which you can interact with `dragula`.\n\n### `dragulaService.createGroup(name, options)`\n\nNOTE: formerly known as `setOptions()`\n\nCreates a group named `name`, with an\n[options](#dragulaoptions) object.\n\n### `dragulaService.find(name: string)`\n\nReturns a `Group` named `name`, if there is one. A `Group` contains the following\nproperties.\n\n- `name` is the name that identifies the group\n- `drake` is the raw `drake` instance itself\n- `options` is the options object used to create the drake. Modifying it won't\n  do anything useful.\n\n### `dragulaService.destroy(name)`\n\nDestroys a `Group` named `name` and its associated `drake` instance. Silently\nreturns if the group does not exist.\n\n### DragulaOptions\n\nRefer to the documentation for\n[dragula](https://github.com/bevacqua/dragula#readme) to learn more about the\nnative options.\n\nAll of the native options work with ng2-dragula. However, there is one addition:\n\n#### `copyItem: \u003cT\u003e(item: T) =\u003e T`\n\nWhen you have:\n\n* `[(dragulaModel)]`\n* `copy` is `true` or a *function that returns true*\n\n... ng2-dragula will have to create a clone of the JS object you picked up. In\nprevious versions of `ng2-dragula`, there was a terribly buggy,\none-size-fits-all clone function. From v2 onwards, you **MUST** provide your own\n`copyItem` function.\n\nIf you have a simple object with no nested values, it could be as simple as:\n\n```ts\n{\n  copy: ...,\n  copyItem: (item: MyType) =\u003e ({ ...item })\n}\n```\n\nThere is a complete example using a `Person` class on the demo page.\n\n## Events\n\nWhenever a `drake` instance is created with the `dragula` directive, there are\nseveral events you can subscribe to via `DragulaService`. Each event emits\na typed object, which you can use to get information about what happened.\n\nRefer to [the Drake events\ndocumentation](https://github.com/bevacqua/dragula#drakeon-events) for more\ninformation about the different events available. Each event follows this\nformat:\n\n```yml\nEvent named: 'drag'\n\nNative dragula:\n  Use: drake.on('drag', listener)\n  Listener arguments: (el, source)\n\nng2-dragula:\n  Method: DragulaService.drag(groupName?: string): Observable\u003c...\u003e\n  Observable of: { name: string; el: Element; source: Element; }\n```\n\nEach supports an optional parameter, `groupName?: string`, which filters events\nto the group you're interested in. This is usually better than getting all\ngroups in one observable.\n\nThe sample below illustrates how you can use destructuring to pull values from\nthe event, and unsubscribe when your component is destroyed.\n\n```html\n\u003cdiv dragula=\"VAMPIRES\"\u003e\u003c/div\u003e\n```\n\n```ts\nimport { Subscription } from 'rxjs';\nimport { DragulaService } from 'ng2-dragula';\n\nexport class MyComponent {\n  // RxJS Subscription is an excellent API for managing many unsubscribe calls.\n  // See note below about unsubscribing.\n  subs = new Subscription();\n\n  constructor(private dragulaService: DragulaService) {\n\n    // These will get events limited to the VAMPIRES group.\n\n    this.subs.add(this.dragulaService.drag(\"VAMPIRES\")\n      .subscribe(({ name, el, source }) =\u003e {\n        // ...\n      })\n    );\n    this.subs.add(this.dragulaService.drop(\"VAMPIRES\")\n      .subscribe(({ name, el, target, source, sibling }) =\u003e {\n        // ...\n      })\n    );\n    // some events have lots of properties, just pick the ones you need\n    this.subs.add(this.dragulaService.dropModel(\"VAMPIRES\")\n      // WHOA\n      // .subscribe(({ name, el, target, source, sibling, sourceModel, targetModel, item }) =\u003e {\n      .subscribe(({ sourceModel, targetModel, item }) =\u003e {\n        // ...\n      })\n    );\n\n    // You can also get all events, not limited to a particular group\n    this.subs.add(this.dragulaService.drop()\n      .subscribe(({ name, el, target, source, sibling }) =\u003e {\n        // ...\n      })\n    );\n  }\n\n  ngOnDestroy() {\n    // destroy all the subscriptions at once\n    this.subs.unsubscribe();\n  }\n}\n\n```\n\n**NOTE: You should always unsubscribe each time you listen to an event.** This\nis especially true for a component, which should tear itself down completely in\n`ngOnDestroy`, including any subscriptions. It might not be necessary if you\nhave a global singleton service (which is never destroyed) doing the\nsubscribing.\n\nYou can also engineer your use of events to avoid subscribing in the first\nplace:\n\n```ts\nimport { merge } from 'rxjs';\nimport { mapTo, startWith } from 'rxjs/operators';\n\ndragStart$ = this.dragulaService.drag(\"VAMPIRES\").pipe(mapTo(true));\ndragEnd$ = this.dragulaService.dragend(\"VAMPIRES\").pipe(mapTo(false));\nisDragging$ = merge(dragStart$, dragEnd$).pipe(startWith(false));\n\n// html: [class.dragging]=\"isDragging$ | async\"\n```\n\n## Special Events for `ng2-dragula`\n\nThe `dropModel(name?: string)` and `removeModel(name?: string)` events are only active when you have supplied `[dragulaModel]`.\n\n| Event Name      | Listener Arguments                                           | Event Description                                                                        |\n| :-------------- | :-------------------------:                                  | :--------------------------------------------------------------------------------------- |\n| dropModel | { type, el, target, source, item, sourceModel, targetModel, sourceIndex, targetIndex } | same as normal drop, but with updated models + the item that was dropped |\n| removeModel | { type, el, container, source, item, sourceModel, sourceIndex } | same as normal remove, but with updated model + the item that got removed |\n\n# Classic Blunders\n\nThere are a number of very common issues filed against this repo. You will be\nmocked terribly if you file a bug and it turns out you made one of these\nblunders and it wasn't a bug at all.\n\n### 1. Do not put `[dragula]` or `[(dragulaModel)]` on the same element as `*ngFor`.\n\n**WRONG:**\n\n```html\n\u003cdiv class=\"container\"\u003e\n  \u003cdiv *ngFor=\"let x of list\"\n       dragula=\"WRONG\" [(dragulaModel)]=\"list\"\u003e...\u003c/div\u003e\n\u003c/div\u003e\n```\n\n**RIGHT:**\n\n```html\n\u003cdiv class=\"container\" dragula=\"RIGHT\" [(dragulaModel)]=\"list\"\u003e\n  \u003cdiv *ngFor=\"let x of list\"\u003e...\u003c/div\u003e\n\u003c/div\u003e\n```\n\n### 2. Do not add any child elements that aren't meant to be draggable\n\n**WRONG:**\n```html\n\u003cdiv class=\"container\" dragula=\"WRONG\" [(dragulaModel)]=\"list\"\u003e\n  \u003ch2\u003eWRONG: This header will mess up everything, and you will\n      get really weird bugs on drop\u003c/h2\u003e\n  \u003cdiv *ngFor=\"let x of list\"\u003e...\u003c/div\u003e\n\u003c/div\u003e\n```\n\n**RIGHT:**\n```html\n\u003ch2\u003eThis header will not be draggable or affect drags at all.\u003c/h2\u003e\n\u003cdiv class=\"container\" dragula=\"RIGHT\" [(dragulaModel)]=\"list\"\u003e\n  \u003cdiv *ngFor=\"let x of list\"\u003e...\u003c/div\u003e\n\u003c/div\u003e\n```\n\n# Alternatives\n\nThere are hundreds of other libraries that do this. Some notable ones:\n\n- [@angular-skyhook](https://cormacrelf.github.io/angular-skyhook/examples/), specifically with the sortable. Also by me (@cormacrelf).\n- [@angular/cdk/drag-drop](https://material.angular.io/cdk/categories) -- no documentation yet, but will presumably be well-supported.\n\n# Development\n\n- You must use Yarn \u003e= 1.3. It includes the 'workspaces' feature.\n- Please use [Conventional Commits](https://conventionalcommits.org/) in your commit messages.\n\n#### setup\n\n```sh\nyarn\nyarn workspace ng2-dragula build\n```\n\n#### run tests\n\n```sh\nyarn workspace ng2-dragula test\n# or\nyarn workspace ng2-dragula test:headless\n```\n\n#### run demo server\n\n```sh\n# listens for changes in the library and rebuilds on save\nyarn watch\n# runs demo server\nyarn workspace demo start\n```\n\n#### Publishing a new version\n\n```\nyarn lerna publish\n```\n\n## Credits\n\n- `v1`: Nathan Walker (@NathanWalker)\n- `v1.x`: Dmitriy Shekhovtsov (@valorkin)\n- `v2`: Cormac Relf (@cormacrelf)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalor-software%2Fng2-dragula","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalor-software%2Fng2-dragula","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalor-software%2Fng2-dragula/lists"}