Last active
October 13, 2022 20:41
-
-
Save ali-kamalizade/84c1abe6165523b27a345eb509c9678a to your computer and use it in GitHub Desktop.
Angular CDK drag & drop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div #formDropArea="cdkDropList" class="formFieldsDropArea" cdkDropList (cdkDropListDropped)="onDrop($event)"> | |
<div *ngFor="let formField of formFields; let index = index; trackBy: trackByIndex" class="columns" cdkDrag (cdkDragStarted)="onDragStart()"> | |
<div class="column is-narrow"> | |
<i class="fas fa-fw fa-grip-vertical" cdkDragHandle title="Drag to reorder"></i> | |
</div> | |
<div class="column"> | |
<input [ngModel]="formField.title" (ngModelChange)="onFormFieldChange(formField, $event)""> | |
<button (click)="deleteField(index)">Delete</button> | |
</div> | |
</div> | |
</div> | |
<strong>Elements</strong> | |
<ul cdkDropList cdkDropListSortingDisabled [cdkDropListConnectedTo]="[formDropArea]"> | |
<li *ngFor="let fieldType of choices" cdkDrag (cdkDragStarted)="onDragStart()"> | |
{{ fieldType.title }} | |
</li> | |
</ul> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, Input, Output, EventEmitter } from '@angular/core'; | |
import { CdkDragDrop } from '@angular/cdk/drag-drop'; | |
import { addItemToArray, moveItemWithinArray } from '../pure-dnd.helpers'; | |
type Field = {}; | |
@Component({ | |
selector: 'app-dnd', | |
templateUrl: './dnd.component.html', | |
styleUrls: ['./dnd.component.scss'], | |
changeDetection: ChangeDetectionStrategy.OnPush | |
}) | |
export class DndComponent { | |
@Input() readonly formFields: Field[] = []; | |
@Output() readonly formFieldsChange = new EventEmitter<ReadonlyArray<Field>>(); | |
readonly fieldChoices = [ | |
{ type: 'textarea', title: 'Text field' }, | |
{ type: 'number', title: 'Number field' }, | |
{ type: 'date', title: 'Date field' } | |
]; | |
constructor(private changeDetectorRef: ChangeDetectorRef) {} | |
onFormFieldChange(originalField: Field, updatedField: Field) { | |
this.formFieldsChange.emit(this.formFields.map((field) => (field === originalField ? updatedField : field))); | |
} | |
onDrop(dropEvent: CdkDragDrop<any>) { | |
this.changeDetectorRef.reattach(); | |
const newFieldIndex = dropEvent.currentIndex; | |
if (dropEvent.previousContainer === dropEvent.container) { | |
this.formFieldsChange.emit(moveItemWithinArray(this.formFields, dropEvent.previousIndex, newFieldIndex)); | |
} else { | |
this.formFieldsChange.emit( | |
addItemToArray( | |
{ label: '', type: this.fieldChoices[dropEvent.previousIndex].type, }, | |
this.formFields, | |
newFieldIndex | |
) | |
); | |
} | |
} | |
onDragStart() { | |
this.changeDetectorRef.detach(); | |
} | |
trackByIndex(index: number) { | |
return index; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Place an item from one list into another list. Unlike the native Angular CDK drag and drop, this function does not mutate the original array. | |
*/ | |
export function addItemToArray<T>(newItem: Readonly<T>, targetArray: ReadonlyArray<T>, targetIndex: number): ReadonlyArray<T> { | |
const to = clamp(targetIndex, targetArray.length); | |
return [...targetArray.slice(0, to), newItem, ...targetArray.slice(targetIndex)]; | |
} | |
/** | |
* Move an item within a list. Unlike the native Angular CDK drag and drop, this function does not mutate the original array. | |
*/ | |
export function moveItemWithinArray<T>(currentArray: ReadonlyArray<T>, fromIndex: number, toIndex: number): ReadonlyArray<T> { | |
const from = clamp(fromIndex, currentArray.length - 1); | |
const to = clamp(toIndex, currentArray.length - 1); | |
if (from === to) { | |
return currentArray; | |
} | |
return currentArray.map((item, index) => { | |
if (index === from) { | |
return currentArray[to]; | |
} else { | |
return index === to ? currentArray[from] : item; | |
} | |
}); | |
} | |
/** Clamps a number between zero and a maximum. Copied from Angular CDK */ | |
function clamp(value: number, max: number) { | |
return Math.max(0, Math.min(max, value)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment