1 | # Angular 2 Drag-and-Drop [](https://badge.fury.io/js/ng2-dnd) [](https://www.npmjs.com/package/ng2-dnd)
|
2 | Angular 2 Drag-and-Drop without dependencies.
|
3 |
|
4 | Follow me [](https://twitter.com/akopkokhyants) to be notified about new releases.
|
5 |
|
6 | [](https://travis-ci.org/akserg/ng2-dnd)
|
7 | [](https://david-dm.org/akserg/ng2-dnd)
|
8 | [](https://david-dm.org/akserg/ng2-dnd#info=devDependencies)
|
9 | [](https://snyk.io/test/github/akserg/ng2-dnd)
|
10 |
|
11 | _Some of these APIs and Components are not final and are subject to change!_
|
12 |
|
13 | ## Transpilation to Angular Package Format
|
14 | The library uses [ng-packagr](https://github.com/dherges/ng-packagr) to transpile into the Angular Package Format:
|
15 | - Bundles library in `FESM2015`, `FESM5`, and `UMD` formats
|
16 | - The npm package can be consumed by `Angular CLI`, `Webpack`, or `SystemJS`
|
17 | - Creates type definitions (`.d.ts`)
|
18 | - Generates Ahead-of-Time metadata (`.metadata.json`)
|
19 | - Auto-discovers and bundles secondary entry points such as `@my/foo`, `@my/foo/testing`, `@my/foo/bar`
|
20 |
|
21 | ## Installation
|
22 | ```bash
|
23 | npm install ng2-dnd --save
|
24 | ```
|
25 |
|
26 | ## Demo
|
27 | - Webpack demo available [here](https://angular-dxqjhj.stackblitz.io)
|
28 | - SystemJS demo available [here](http://embed.plnkr.co/JbG8Si)
|
29 |
|
30 | ## Usage
|
31 | If you use SystemJS to load your files, you might have to update your config:
|
32 |
|
33 | ```js
|
34 | System.config({
|
35 | map: {
|
36 | 'ng2-dnd': 'node_modules/ng2-dnd/bundles/ng2-dnd.umd.js'
|
37 | }
|
38 | });
|
39 | ```
|
40 |
|
41 | #### 1. Add the default styles
|
42 | - Import the `style.css` into your web page from `node_modules/ng2-dnd/bundles/style.css`
|
43 |
|
44 | #### 2. Import the `DndModule`
|
45 | Import `DndModule.forRoot()` in the NgModule of your application.
|
46 | The `forRoot` method is a convention for modules that provide a singleton service.
|
47 |
|
48 | ```ts
|
49 | import {BrowserModule} from "@angular/platform-browser";
|
50 | import {NgModule} from '@angular/core';
|
51 | import {DndModule} from 'ng2-dnd';
|
52 |
|
53 | @NgModule({
|
54 | imports: [
|
55 | BrowserModule,
|
56 | DndModule.forRoot()
|
57 | ],
|
58 | bootstrap: [AppComponent]
|
59 | })
|
60 | export class AppModule {
|
61 | }
|
62 | ```
|
63 |
|
64 | If you have multiple NgModules and you use one as a shared NgModule (that you import in all of your other NgModules),
|
65 | don't forget that you can use it to export the `DndModule` that you imported in order to avoid having to import it multiple times.
|
66 |
|
67 | ```ts
|
68 | @NgModule({
|
69 | imports: [
|
70 | BrowserModule,
|
71 | DndModule
|
72 | ],
|
73 | exports: [BrowserModule, DndModule],
|
74 | })
|
75 | export class SharedModule {
|
76 | }
|
77 | ```
|
78 |
|
79 | #### 3. Use Drag-and-Drop operations with no code
|
80 |
|
81 | ```js
|
82 | import {Component} from '@angular/core';
|
83 |
|
84 | @Component({
|
85 | selector: 'simple-dnd',
|
86 | template: `
|
87 | <h4>Simple Drag-and-Drop</h4>
|
88 | <div class="row">
|
89 | <div class="col-sm-3">
|
90 | <div class="panel panel-success">
|
91 | <div class="panel-heading">Available to drag</div>
|
92 | <div class="panel-body">
|
93 | <div class="panel panel-default" dnd-draggable [dragEnabled]="true">
|
94 | <div class="panel-body">
|
95 | <div>Drag Me</div>
|
96 | </div>
|
97 | </div>
|
98 | </div>
|
99 | </div>
|
100 | </div>
|
101 | <div class="col-sm-3">
|
102 | <div dnd-droppable class="panel panel-info" (onDropSuccess)="simpleDrop=$event">
|
103 | <div class="panel-heading">Place to drop</div>
|
104 | <div class="panel-body">
|
105 | <div *ngIf="simpleDrop">Item was dropped here</div>
|
106 | </div>
|
107 | </div>
|
108 | </div>
|
109 | </div>`
|
110 | })
|
111 | export class SimpleDndComponent {
|
112 | simpleDrop: any = null;
|
113 | }
|
114 | ```
|
115 |
|
116 | #### 4. Add handle to restrict draggable zone of component
|
117 |
|
118 | ```js
|
119 | import {Component} from '@angular/core';
|
120 |
|
121 | @Component({
|
122 | selector: 'simple-dnd-handle',
|
123 | template: `
|
124 | <h4>Simple Drag-and-Drop with handle</h4>
|
125 | <div class="row">
|
126 | <div class="col-sm-3">
|
127 | <div class="panel panel-success">
|
128 | <div class="panel-heading">Available to drag</div>
|
129 | <div class="panel-body">
|
130 | <div class="panel panel-default" dnd-draggable [dragEnabled]="true">
|
131 | <div class="panel-body">
|
132 | <div>
|
133 | <span dnd-draggable-handle>=</span>
|
134 | Drag Handle
|
135 | </div>
|
136 | </div>
|
137 | </div>
|
138 | </div>
|
139 | </div>
|
140 | </div>
|
141 | <div class="col-sm-3">
|
142 | <div dnd-droppable class="panel panel-info" (onDropSuccess)="simpleDrop=$event">
|
143 | <div class="panel-heading">Place to drop</div>
|
144 | <div class="panel-body">
|
145 | <div *ngIf="simpleDrop">Item was dropped here</div>
|
146 | </div>
|
147 | </div>
|
148 | </div>
|
149 | </div>`
|
150 | })
|
151 | export class SimpleDndHandleComponent {
|
152 | simpleDrop: any = null;
|
153 | }
|
154 | ```
|
155 |
|
156 | #### 5. Restriction Drag-and-Drop operations with drop zones
|
157 | You can use property *dropZones* (actually an array) to specify in which place you would like to drop the draggable element:
|
158 |
|
159 | ```js
|
160 | import {Component} from '@angular/core';
|
161 |
|
162 | @Component({
|
163 | selector: 'zone-dnd',
|
164 | template: `
|
165 | <h4>Restricted Drag-and-Drop with zones</h4>
|
166 | <div class="row">
|
167 | <div class="col-sm-3">
|
168 | <div class="panel panel-primary">
|
169 | <div class="panel-heading">Available to drag</div>
|
170 | <div class="panel-body">
|
171 | <div class="panel panel-default" dnd-draggable [dragEnabled]="true" [dropZones]="['zone1']">
|
172 | <div class="panel-body">
|
173 | <div>Drag Me</div>
|
174 | <div>Zone 1 only</div>
|
175 | </div>
|
176 | </div>
|
177 | </div>
|
178 | </div>
|
179 |
|
180 | <div class="panel panel-success">
|
181 | <div class="panel-heading">Available to drag</div>
|
182 | <div class="panel-body">
|
183 | <div class="panel panel-default" dnd-draggable [dragEnabled]="true" [dropZones]="['zone1', 'zone2']">
|
184 | <div class="panel-body">
|
185 | <div>Drag Me</div>
|
186 | <div>Zone 1 & 2</div>
|
187 | </div>
|
188 | </div>
|
189 | </div>
|
190 | </div>
|
191 | </div>
|
192 | <div class="col-sm-3">
|
193 | <div dnd-droppable class="panel panel-info" [dropZones]="['zone1']" (onDropSuccess)="restrictedDrop1=$event">
|
194 | <div class="panel-heading">Zone 1</div>
|
195 | <div class="panel-body">
|
196 | <div *ngIf="restrictedDrop1">Item was dropped here</div>
|
197 | </div>
|
198 | </div>
|
199 | </div>
|
200 | <div class="col-sm-3">
|
201 | <div dnd-droppable class="panel panel-warning" [dropZones]="['zone2']" (onDropSuccess)="restrictedDrop2=$event">
|
202 | <div class="panel-heading">Zone 2</div>
|
203 | <div class="panel-body">
|
204 | <div *ngIf="restrictedDrop2">Item was dropped here</div>
|
205 | </div>
|
206 | </div>
|
207 | </div>
|
208 | </div>`
|
209 | })
|
210 | export class ZoneDndComponent {
|
211 | restrictedDrop1: any = null;
|
212 | restrictedDrop2: any = null;
|
213 | }
|
214 | ```
|
215 |
|
216 | #### 6. Transfer custom data via Drag-and-Drop
|
217 | You can transfer data from draggable to droppable component via *dragData* property of Draggable component:
|
218 |
|
219 | ```js
|
220 | import {Component} from '@angular/core';
|
221 |
|
222 | @Component({
|
223 | selector: 'custom-data-dnd',
|
224 | template: `
|
225 | <h4>Transfer custom data in Drag-and-Drop</h4>
|
226 | <div class="row">
|
227 | <div class="col-sm-3">
|
228 | <div class="panel panel-success">
|
229 | <div class="panel-heading">Available to drag</div>
|
230 | <div class="panel-body">
|
231 | <div class="panel panel-default" dnd-draggable [dragEnabled]="true" [dragData]="transferData">
|
232 | <div class="panel-body">
|
233 | <div>Drag Me</div>
|
234 | <div>{{transferData | json}}</div>
|
235 | </div>
|
236 | </div>
|
237 | </div>
|
238 | </div>
|
239 | </div>
|
240 | <div class="col-sm-3">
|
241 | <div dnd-droppable class="panel panel-info" (onDropSuccess)="transferDataSuccess($event)">
|
242 | <div class="panel-heading">Place to drop (Items:{{receivedData.length}})</div>
|
243 | <div class="panel-body">
|
244 | <div [hidden]="!receivedData.length > 0" *ngFor="let data of receivedData">{{data | json}}</div>
|
245 | </div>
|
246 | </div>
|
247 | </div>
|
248 | </div>`
|
249 | })
|
250 | export class CustomDataDndComponent {
|
251 | transferData: Object = {id: 1, msg: 'Hello'};
|
252 | receivedData: Array<any> = [];
|
253 |
|
254 | transferDataSuccess($event: any) {
|
255 | this.receivedData.push($event);
|
256 | }
|
257 | }
|
258 | ```
|
259 |
|
260 | #### 7. Use a custom function to determine where dropping is allowed
|
261 | For use-cases when a static set of `dropZone`s is not possible, a custom function can be used to dynamically determine whether an item can be dropped or not. To achieve that, set the `allowDrop` property to this boolean function.
|
262 |
|
263 | In the following example, we have two containers that only accept numbers that are multiples of a user-input base integer. `dropZone`s are not helpful here because they are static, whereas the user input is dynamic.
|
264 |
|
265 | ```js
|
266 | import { Component } from '@angular/core';
|
267 |
|
268 | @Component({
|
269 | selector: 'custom-function-dnd',
|
270 | template: `
|
271 | <h4>Use a custom function to determine where dropping is allowed</h4>
|
272 | <div class="row">
|
273 | <div class="col-sm-3">
|
274 | <div class="panel panel-success">
|
275 | <div class="panel-heading">Available to drag</div>
|
276 | <div class="panel-body">
|
277 | <div class="panel panel-default" dnd-draggable [dragData]="6">
|
278 | <div class="panel-body">dragData = 6</div>
|
279 | </div>
|
280 | <div class="panel panel-default" dnd-draggable [dragData]="10">
|
281 | <div class="panel-body">dragData = 10</div>
|
282 | </div>
|
283 | <div class="panel panel-default" dnd-draggable [dragData]="30">
|
284 | <div class="panel-body">dragData = 30</div>
|
285 | </div>
|
286 | </div>
|
287 | </div>
|
288 | </div>
|
289 | <div class="col-sm-6">
|
290 | <pre>allowDropFunction(baseInteger: any): any {{ '{' }}
|
291 | return (dragData: any) => dragData % baseInteger === 0;
|
292 | {{ '}' }}</pre>
|
293 | <div class="row">
|
294 | <div class="col-sm-6">
|
295 | <div dnd-droppable class="panel panel-info" [allowDrop]="allowDropFunction(box1Integer)" (onDropSuccess)="addTobox1Items($event)">
|
296 | <div class="panel-heading">
|
297 | Multiples of
|
298 | <input type="number" [(ngModel)]="box1Integer" style="width: 4em">
|
299 | only
|
300 | </div>
|
301 | <div class="panel-body">
|
302 | <div *ngFor="let item of box1Items">dragData = {{item}}</div>
|
303 | </div>
|
304 | </div>
|
305 | </div>
|
306 | <div class="col-sm-6">
|
307 | <div dnd-droppable class="panel panel-warning" [allowDrop]="allowDropFunction(box2Integer)" (onDropSuccess)="addTobox2Items($event)">
|
308 | <div class="panel-heading">
|
309 | Multiples of
|
310 | <input type="number" [(ngModel)]="box2Integer" style="width: 4em">
|
311 | only
|
312 | </div>
|
313 | <div class="panel-body">
|
314 | <div *ngFor="let item of box2Items">dragData = {{item}}</div>
|
315 | </div>
|
316 | </div>
|
317 | </div>
|
318 | </div>
|
319 | </div>
|
320 | </div>
|
321 | `
|
322 | })
|
323 | export class CustomFunctionDndComponent {
|
324 | box1Integer: number = 3;
|
325 | box2Integer: number = 10;
|
326 |
|
327 | box1Items: string[] = [];
|
328 | box2Items: string[] = [];
|
329 |
|
330 | allowDropFunction(baseInteger: number): any {
|
331 | return (dragData: any) => dragData % baseInteger === 0;
|
332 | }
|
333 |
|
334 | addTobox1Items($event: any) {
|
335 | this.box1Items.push($event.dragData);
|
336 | }
|
337 |
|
338 | addTobox2Items($event: any) {
|
339 | this.box2Items.push($event.dragData);
|
340 | }
|
341 | }
|
342 | ```
|
343 |
|
344 | #### 8. Shopping basket with Drag-and-Drop
|
345 | Here is an example of shopping backet with products adding via drag and drop operation:
|
346 |
|
347 | ```js
|
348 | import { Component } from '@angular/core';
|
349 |
|
350 | @Component({
|
351 | selector: 'shoping-basket-dnd',
|
352 | template: `
|
353 | <h4>Drag-and-Drop - Shopping basket</h4>
|
354 | <div class="row">
|
355 |
|
356 | <div class="col-sm-3">
|
357 | <div class="panel panel-success">
|
358 | <div class="panel-heading">Available products</div>
|
359 | <div class="panel-body">
|
360 | <div *ngFor="let product of availableProducts" class="panel panel-default"
|
361 | dnd-draggable [dragEnabled]="product.quantity>0" [dragData]="product" (onDragSuccess)="orderedProduct($event)" [dropZones]="['demo1']">
|
362 | <div class="panel-body">
|
363 | <div [hidden]="product.quantity===0">{{product.name}} - \${{product.cost}}<br>(available: {{product.quantity}})</div>
|
364 | <div [hidden]="product.quantity>0"><del>{{product.name}}</del><br>(NOT available)</div>
|
365 | </div>
|
366 | </div>
|
367 | </div>
|
368 | </div>
|
369 | </div>
|
370 | <div class="col-sm-3">
|
371 | <div dnd-droppable (onDropSuccess)="addToBasket($event)" [dropZones]="['demo1']" class="panel panel-info">
|
372 | <div class="panel-heading">Shopping Basket<br>(to pay: \${{totalCost()}})</div>
|
373 | <div class="panel-body">
|
374 | <div *ngFor="let product of shoppingBasket" class="panel panel-default">
|
375 | <div class="panel-body">
|
376 | {{product.name}}<br>(ordered: {{product.quantity}}<br>cost: \${{product.cost * product.quantity}})
|
377 | </div>
|
378 | </div>
|
379 | </div>
|
380 | </div>
|
381 | </div>
|
382 | </div>`
|
383 | })
|
384 | export class ShoppingBasketDndComponent {
|
385 | availableProducts: Array<Product> = [];
|
386 | shoppingBasket: Array<Product> = [];
|
387 |
|
388 | constructor() {
|
389 | this.availableProducts.push(new Product('Blue Shoes', 3, 35));
|
390 | this.availableProducts.push(new Product('Good Jacket', 1, 90));
|
391 | this.availableProducts.push(new Product('Red Shirt', 5, 12));
|
392 | this.availableProducts.push(new Product('Blue Jeans', 4, 60));
|
393 | }
|
394 |
|
395 | orderedProduct($event: any) {
|
396 | let orderedProduct: Product = $event.dragData;
|
397 | orderedProduct.quantity--;
|
398 | }
|
399 |
|
400 | addToBasket($event: any) {
|
401 | let newProduct: Product = $event.dragData;
|
402 | for (let indx in this.shoppingBasket) {
|
403 | let product: Product = this.shoppingBasket[indx];
|
404 | if (product.name === newProduct.name) {
|
405 | product.quantity++;
|
406 | return;
|
407 | }
|
408 | }
|
409 | this.shoppingBasket.push(new Product(newProduct.name, 1, newProduct.cost));
|
410 | this.shoppingBasket.sort((a: Product, b: Product) => {
|
411 | return a.name.localeCompare(b.name);
|
412 | });
|
413 | }
|
414 |
|
415 | totalCost(): number {
|
416 | let cost: number = 0;
|
417 | for (let indx in this.shoppingBasket) {
|
418 | let product: Product = this.shoppingBasket[indx];
|
419 | cost += (product.cost * product.quantity);
|
420 | }
|
421 | return cost;
|
422 | }
|
423 | }
|
424 |
|
425 | class Product {
|
426 | constructor(public name: string, public quantity: number, public cost: number) {}
|
427 | }
|
428 | ```
|
429 |
|
430 | #### 9. Simple sortable with Drag-and-Drop
|
431 | Here is an example of simple sortable of favorite drinks moving in container via drag and drop operation:
|
432 |
|
433 | ```js
|
434 | import {Component} from '@angular/core';
|
435 |
|
436 | @Component({
|
437 | selector: 'simple-sortable',
|
438 | template: `
|
439 | <h4>Simple sortable</h4>
|
440 | <div class="row">
|
441 | <div class="col-sm-3">
|
442 | <div class="panel panel-success">
|
443 | <div class="panel-heading">
|
444 | Favorite drinks
|
445 | </div>
|
446 | <div class="panel-body">
|
447 | <ul class="list-group" dnd-sortable-container [sortableData]="listOne">
|
448 | <li *ngFor="let item of listOne; let i = index" class="list-group-item" dnd-sortable [sortableIndex]="i">{{item}}</li>
|
449 | </ul>
|
450 | </div>
|
451 | </div>
|
452 | </div>
|
453 | <div class="col-sm-6">
|
454 | <div class="panel panel-default">
|
455 | <div class="panel-body">
|
456 | My prefences:<br/>
|
457 | <span *ngFor="let item of listOne; let i = index">{{i + 1}}) {{item}}<br/></span>
|
458 | </div>
|
459 | </div>
|
460 | </div>
|
461 | </div>`
|
462 | })
|
463 | export class SimpleSortableComponent {
|
464 | listOne: Array<string> = ['Coffee', 'Orange Juice', 'Red Wine', 'Unhealty drink!', 'Water'];
|
465 | }
|
466 | ```
|
467 |
|
468 |
|
469 | #### 10. Simple sortable with Drag-and-Drop handle
|
470 | Add handle to restict grip zone of sortable component.
|
471 |
|
472 | ```js
|
473 | import {Component} from '@angular/core';
|
474 |
|
475 | @Component({
|
476 | selector: 'simple-sortable-handle',
|
477 | template: `
|
478 | <h4>Simple sortable handle</h4>
|
479 | <div class="row">
|
480 | <div class="col-sm-3">
|
481 | <div class="panel panel-success">
|
482 | <div class="panel-heading">
|
483 | Favorite drinks
|
484 | </div>
|
485 | <div class="panel-body">
|
486 | <ul class="list-group" dnd-sortable-container [sortableData]="listOne">
|
487 | <li *ngFor="let item of listOne; let i = index" class="list-group-item" dnd-sortable [sortableIndex]="i">
|
488 | <span dnd-sortable-handle>=</span>
|
489 | {{item}}
|
490 | </li>
|
491 | </ul>
|
492 | </div>
|
493 | </div>
|
494 | </div>
|
495 | <div class="col-sm-6">
|
496 | <div class="panel panel-default">
|
497 | <div class="panel-body">
|
498 | My prefences:<br/>
|
499 | <span *ngFor="let item of listOne; let i = index">{{i + 1}}) {{item}}<br/></span>
|
500 | </div>
|
501 | </div>
|
502 | </div>
|
503 | </div>`
|
504 | })
|
505 | export class SimpleSortableHandleComponent {
|
506 | listOne: Array<string> = ['Coffee', 'Orange Juice', 'Red Wine', 'Unhealty drink!', 'Water'];
|
507 | }
|
508 | ```
|
509 |
|
510 | #### 11. Simple sortable With Drop into recycle bin
|
511 | Here is an example of multi list sortable of boxers moving in container and between containers via drag and drop operation:
|
512 |
|
513 | ```js
|
514 | import {Component} from '@angular/core';
|
515 |
|
516 | @Component({
|
517 | selector: 'recycle-multi-sortable',
|
518 | template: `
|
519 | <h4>Simple sortable With Drop into recycle bin</h4>
|
520 | <div class="row">
|
521 | <div class="col-sm-3">
|
522 | <div class="panel panel-success">
|
523 | <div class="panel-heading">
|
524 | Favorite drinks
|
525 | </div>
|
526 | <div class="panel-body" dnd-sortable-container [sortableData]="listOne" [dropZones]="['delete-dropZone']">
|
527 | <ul class="list-group">
|
528 | <li *ngFor="let item of listOne; let i = index" class="list-group-item"
|
529 | dnd-sortable [sortableIndex]="i">{{item}}</li>
|
530 | </ul>
|
531 | </div>
|
532 | </div>
|
533 | </div>
|
534 | <div class="col-sm-6">
|
535 | <div class="panel panel-default">
|
536 | <div class="panel-body" dnd-sortable-container [dropZones]="['delete-dropZone']" [sortableData]="listRecycled">
|
537 | Recycle bin: Drag into me to delete it<br/>
|
538 | </div>
|
539 | </div>
|
540 | <div *ngIf="listRecycled.length">
|
541 | <b>Recycled:</b> <span>{{listRecycled.toString()}} </span>
|
542 | </div>
|
543 | </div>
|
544 | </div>`
|
545 | })
|
546 | export class RecycleMultiSortableComponent {
|
547 | listOne: Array<string> = ['Coffee', 'Orange Juice', 'Red Wine', 'Unhealty drink!', 'Water'];
|
548 | listRecycled: Array<string> = [];
|
549 | }
|
550 | ```
|
551 |
|
552 | #### 12. Simple sortable With Drop into something, without delete it
|
553 | Here is an example of simple sortable list of items copying in target container:
|
554 |
|
555 | ```js
|
556 | import {Component} from '@angular/core';
|
557 |
|
558 | @Component({
|
559 | selector: 'simple-sortable-copy',
|
560 | template: `
|
561 | <h4>Simple sortable With Drop into something, without delete it</h4>
|
562 | <div class="row">
|
563 | <div class="col-sm-3">
|
564 | <div class="panel panel-warning"
|
565 | dnd-sortable-container [sortableData]="sourceList" [dropZones]="['source-dropZone']">
|
566 | <div class="panel-heading">Source List</div>
|
567 | <div class="panel-body">
|
568 | <ul class="list-group">
|
569 | <li *ngFor="let source of sourceList; let x = index" class="list-group-item"
|
570 | dnd-sortable [sortableIndex]="x" [dragEnabled]="true"
|
571 | [dragData]="source">{{source.name}}</li>
|
572 | </ul>
|
573 | </div>
|
574 | </div>
|
575 | </div>
|
576 | <div class="col-sm-6">
|
577 | <div class="panel panel-info">
|
578 | <div class="panel-heading">Target List</div>
|
579 | <div class="panel-body" dnd-droppable (onDropSuccess)="addTo($event)" [dropZones]="['source-dropZone']">
|
580 | <ul class="list-group">
|
581 | <li *ngFor="let target of targetList" class="list-group-item">
|
582 | {{target.name}}
|
583 | </li>
|
584 | </ul>
|
585 | </div>
|
586 | </div>
|
587 | </div>
|
588 | </div>`
|
589 | })
|
590 | export class SimpleSortableCopyComponent {
|
591 |
|
592 | sourceList: Widget[] = [
|
593 | new Widget('1'), new Widget('2'),
|
594 | new Widget('3'), new Widget('4'),
|
595 | new Widget('5'), new Widget('6')
|
596 | ];
|
597 |
|
598 | targetList: Widget[] = [];
|
599 | addTo($event: any) {
|
600 | this.targetList.push($event.dragData);
|
601 | }
|
602 | }
|
603 |
|
604 | class Widget {
|
605 | constructor(public name: string) {}
|
606 | }
|
607 | ```
|
608 |
|
609 | #### 13. Multi list sortable between containers
|
610 | Here is an example of multi list sortable of boxers moving in container and between containers via drag and drop operation:
|
611 |
|
612 | ```js
|
613 | import {Component} from '@angular/core';
|
614 |
|
615 | @Component({
|
616 | selector: 'embedded-sortable',
|
617 | template: `
|
618 | <h4>Move items between multi list sortable containers</h4>
|
619 | <div class="row">
|
620 | <div class="col-sm-3">
|
621 | Drag Containers <input type="checkbox" [(ngModel)]="dragOperation"/>
|
622 | <div dnd-sortable-container [sortableData]="containers" [dropZones]="['container-dropZone']">
|
623 | <div class="col-sm3"
|
624 | *ngFor="let container of containers; let i = index"
|
625 | dnd-sortable [sortableIndex]="i" [dragEnabled]="dragOperation">
|
626 | <div class="panel panel-warning"
|
627 | dnd-sortable-container [sortableData]="container.widgets" [dropZones]="['widget-dropZone']">
|
628 | <div class="panel-heading">
|
629 | {{container.id}} - {{container.name}}
|
630 | </div>
|
631 | <div class="panel-body">
|
632 | <ul class="list-group">
|
633 | <li *ngFor="let widget of container.widgets; let x = index" class="list-group-item"
|
634 | dnd-sortable [sortableIndex]="x" [dragEnabled]="!dragOperation"
|
635 | [dragData]="widget">{{widget.name}}</li>
|
636 | </ul>
|
637 | </div>
|
638 | </div>
|
639 | </div>
|
640 | </div>
|
641 | </div>
|
642 | <div class="col-sm-6">
|
643 | <div class="panel panel-info">
|
644 | <div class="panel-heading">Widgets</div>
|
645 | <div class="panel-body" dnd-droppable (onDropSuccess)="addTo($event)" [dropZones]="['widget-dropZone']">
|
646 | <div *ngFor="let widget of widgets" class="panel panel-default">
|
647 | <div class="panel-body">
|
648 | {{widget.name}}
|
649 | </div>
|
650 | </div>
|
651 | </div>
|
652 | </div>
|
653 | </div>
|
654 | </div>`
|
655 | })
|
656 | export class EmbeddedSortableComponent {
|
657 | dragOperation: boolean = false;
|
658 |
|
659 | containers: Array<Container> = [
|
660 | new Container(1, 'Container 1', [new Widget('1'), new Widget('2')]),
|
661 | new Container(2, 'Container 2', [new Widget('3'), new Widget('4')]),
|
662 | new Container(3, 'Container 3', [new Widget('5'), new Widget('6')])
|
663 | ];
|
664 |
|
665 | widgets: Array<Widget> = [];
|
666 | addTo($event: any) {
|
667 | if ($event) {
|
668 | this.widgets.push($event.dragData);
|
669 | }
|
670 | }
|
671 | }
|
672 |
|
673 | class Container {
|
674 | constructor(public id: number, public name: string, public widgets: Array<Widget>) {}
|
675 | }
|
676 |
|
677 | class Widget {
|
678 | constructor(public name: string) {}
|
679 | }
|
680 | ```
|
681 |
|
682 | #### 14. Simple FormArray sortable with Drag-and-Drop
|
683 | Here is an example of simple sortable of favorite drinks moving in container via drag and drop operation but using FormArray instead of Array:
|
684 |
|
685 | ```js
|
686 | import {Component} from '@angular/core';
|
687 | import {FormArray, FormControl} from '@angular/forms';
|
688 |
|
689 | @Component({
|
690 | selector: 'simple-formarray-sortable',
|
691 | template: `
|
692 | <h4>Simple FormArray sortable</h4>
|
693 | <div class="row">
|
694 | <div class="col-sm-3">
|
695 | <div class="panel panel-success">
|
696 | <div class="panel-heading">
|
697 | Favorite drinks
|
698 | </div>
|
699 | <div class="panel-body">
|
700 | <ul class="list-group" dnd-sortable-container [sortableData]="listOne">
|
701 | <li *ngFor="let item of listOne.controls; let i = index" class="list-group-item" dnd-sortable [sortableIndex]="i"><input type="text" [formControl]="item"></li>
|
702 | </ul>
|
703 | </div>
|
704 | </div>
|
705 | </div>
|
706 | <div class="col-sm-6">
|
707 | <div class="panel panel-default">
|
708 | <div class="panel-body">
|
709 | My prefences:<br/>
|
710 | <span *ngFor="let item of listOne.controls; let i = index">{{i + 1}}) {{item.value}}<br/></span>
|
711 | </div>
|
712 | </div>
|
713 | </div>
|
714 | </div>`
|
715 | })
|
716 | export class SimpleFormArraySortableComponent {
|
717 | listOne: FormArray = new FormArray([
|
718 | new FormControl('Coffee'),
|
719 | new FormControl('Orange Juice'),
|
720 | new FormControl('Red Wine'),
|
721 | new FormControl('Unhealty drink!'),
|
722 | new FormControl('Water')
|
723 | ]);
|
724 | }
|
725 | ```
|
726 |
|
727 | ## How to pass multiple data in dragData while dragging ?
|
728 |
|
729 | 1) As an array:
|
730 |
|
731 | ``` html
|
732 | [dragData]="[aComponent,'component-in-bar']"
|
733 | ```
|
734 |
|
735 | ``` javascript
|
736 | loadComponent($event){
|
737 | console.log($event.dragData[0]); // aComponent
|
738 | console.log($event.dragData[1]); // 'component-in-bar' OR 'component-in-designer'
|
739 | }
|
740 | ```
|
741 |
|
742 | 2) As an object:
|
743 |
|
744 | ``` html
|
745 | [dragData]="{component: aComponent, location: 'component-in-bar'}"
|
746 | ```
|
747 |
|
748 | ``` javascript
|
749 | loadComponent($event){
|
750 | console.log($event.dragData.component); // aComponent
|
751 | console.log($event.dragData.location); // 'component-in-bar' OR 'component-in-designer'
|
752 | }
|
753 | ```
|
754 |
|
755 | # Retreiving files in a drop zone
|
756 |
|
757 | Since it is possible to drag and drop one or more files to a drop zone, you need to handle the incoming files.
|
758 |
|
759 | ```js
|
760 | import {Component} from '@angular/core';
|
761 | import {Http, Headers} from '@angular/http';
|
762 | import {DND_PROVIDERS, DND_DIRECTIVES} from 'ng2-dnd/ng2-dnd';
|
763 | import {bootstrap} from '@angular/platform-browser-dynamic';
|
764 |
|
765 | bootstrap(AppComponent, [
|
766 | DND_PROVIDERS // It is required to have 1 unique instance of your service
|
767 | ]);
|
768 |
|
769 | @Component({
|
770 | selector: 'app',
|
771 | directives: [DND_DIRECTIVES],
|
772 | template: `
|
773 | <h4>Simple Drag-and-Drop</h4>
|
774 | <div class="row">
|
775 |
|
776 | <div class="col-sm-3">
|
777 | <div dnd-droppable class="panel panel-info"
|
778 | (onDropSuccess)="transferDataSuccess($event)">>
|
779 | <div class="panel-heading">Place to drop</div>
|
780 | <div class="panel-body">
|
781 | </div>
|
782 | </div>
|
783 | </div>
|
784 | </div>
|
785 | `
|
786 | })
|
787 | export class AppComponent {
|
788 |
|
789 | constructor(private _http: Http) { }
|
790 |
|
791 | /**
|
792 | * The $event is a structure:
|
793 | * {
|
794 | * dragData: any,
|
795 | * mouseEvent: MouseEvent
|
796 | * }
|
797 | */
|
798 | transferDataSuccess($event) {
|
799 | // let attachmentUploadUrl = 'assets/data/offerspec/offerspec.json';
|
800 | // loading the FileList from the dataTransfer
|
801 | let dataTransfer: DataTransfer = $event.mouseEvent.dataTransfer;
|
802 | if (dataTransfer && dataTransfer.files) {
|
803 |
|
804 | // needed to support posting binaries and usual form values
|
805 | let headers = new Headers();
|
806 | headers.append('Content-Type', 'multipart/form-data');
|
807 |
|
808 | let files: FileList = dataTransfer.files;
|
809 |
|
810 | // uploading the files one by one asynchrounusly
|
811 | for (let i = 0; i < files.length; i++) {
|
812 | let file: File = files[i];
|
813 |
|
814 | // just for debugging
|
815 | console.log('Name: ' + file.name + '\n Type: ' + file.type + '\n Size: ' + file.size + '\n Date: ' + file.lastModifiedDate);
|
816 |
|
817 | // collecting the data to post
|
818 | var data = new FormData();
|
819 | data.append('file', file);
|
820 | data.append('fileName', file.name);
|
821 | data.append('fileSize', file.size);
|
822 | data.append('fileType', file.type);
|
823 | data.append('fileLastMod', file.lastModifiedDate);
|
824 |
|
825 | // posting the data
|
826 | this._http
|
827 | .post(attachmentUploadUrl, data, {
|
828 | headers: headers
|
829 | })
|
830 | .toPromise()
|
831 | .catch(reason => {
|
832 | console.log(JSON.stringify(reason));
|
833 | });
|
834 | }
|
835 | }
|
836 | }
|
837 | }
|
838 |
|
839 | # Credits
|
840 | - [Francesco Cina](https://github.com/ufoscout)
|
841 | - [Valerii Kuznetsov](https://github.com/solival)
|
842 | - [Shane Oborn](https://github.com/obosha)
|
843 | - [Juergen Gutsch](https://github.com/JuergenGutsch)
|
844 | - [Damjan Cilenšek](https://github.com/loudandwicked)
|
845 |
|
846 | # License
|
847 | [MIT](/LICENSE)
|
848 |
|
\ | No newline at end of file |