Angular Uygulamalarınızda ng2-dragula ile Sürükle ve Bırak Özelliği
Modern web uygulamalarında kullanıcı deneyimi (UX), başarının anahtarlarından biridir. Kullanıcıların arayüzle etkileşimini kolaylaştıran ve sezgisel hale getiren özellikler, uygulamanın benimsenme oranını artırır. Bu özelliklerden biri de “sürükle ve bırak” (Drag & Drop) işlevselliğidir. Dosya yüklemelerinden, liste sıralamalarına, panolar arası görev taşımalardan, görsel düzenleyicilere kadar pek çok senaryoda sürükle ve bırak, kullanıcıların iş akışını önemli ölçüde hızlandırır.
Angular gibi güçlü bir ön yüz framework’ü ile uygulama geliştirirken, sürükle ve bırak gibi karmaşık görünen özellikleri entegre etmek için hazır ve güvenilir çözümlere ihtiyaç duyarız. İşte tam bu noktada ng2-dragula devreye giriyor. ng2-dragula, popüler Dragula kütüphanesini Angular ekosistemine taşıyan, kullanımı kolay ve esnek bir çözümdür. Bu makalede, ng2-dragula‘nın ne olduğunu, neden tercih edilmesi gerektiğini, temel ve ileri düzey kullanımlarını, özelleştirme seçeneklerini, gerçek dünya senaryolarını ve en iyi uygulamalarını detaylı bir şekilde inceleyeceğiz.
Neden ng2-dragula?
Angular uygulamalarınıza sürükle ve bırak özelliği eklemek için birden fazla yol vardır. Kendi çözümünüzü sıfırdan yazabilir, @angular/cdk/drag-drop gibi Angular’ın resmi bileşen kütüphanesini kullanabilir veya ng2-dragula gibi üçüncü taraf kütüphanelere yönelebilirsiniz. Peki, ng2-dragula‘yı diğerlerinden ayıran ve onu cazip kılan özellikler nelerdir?
Öncelikle, ng2-dragula, Dragula kütüphanesinin kanıtlanmış sağlamlığını ve basitliğini Angular dünyasına taşır. Dragula, özellikle listeler arasında veya aynı liste içinde öğeleri sürükleyip bırakma işlemleri için tasarlanmış, hafif ve bağımlılığı az bir JavaScript kütüphanesidir. ng2-dragula bu temeli alarak, Angular’ın modüler yapısına ve bileşen tabanlı mimarisine mükemmel bir şekilde entegre olur.
ng2-dragula‘nın temel avantajları şunlardır:
* Basitlik ve Hız: Kurulumu ve kullanımı son derece kolaydır. Sadece birkaç satır kod ile sürükle ve bırak işlevselliğini uygulamanıza dahil edebilirsiniz.
* Esneklik: Tek bir kapsayıcı içinde sıralama, birden fazla kapsayıcı arasında öğe taşıma veya belirli kurallara göre sürükleme/bırakma işlemlerini kontrol etme gibi çeşitli senaryoları destekler.
* Olay Tabanlı Mimari: Sürükleme, bırakma, öğe üzerine gelme gibi çeşitli olayları dinleyerek uygulamanızın davranışını özelleştirebilirsiniz. Bu, karmaşık iş mantıklarını entegre etmek için güçlü bir mekanizma sunar.
* Görsel Özelleştirme: Sürüklenen öğelerin, bırakma bölgelerinin ve geçici yer tutucuların görünümünü CSS ile kolayca değiştirebilirsiniz.
* Veri Odaklı Yaklaşım: dragulaModel direktifi sayesinde, sürükle ve bırak işlemleri doğrudan veri modelinizle senkronize olur. Bu, uygulamanızın durumunu yönetmeyi basitleştirir.
Bu avantajlar, özellikle hızlı prototipleme yaparken veya karmaşık olmayan ancak sağlam bir sürükle ve bırak çözümüne ihtiyaç duyduğunuzda ng2-dragula‘yı ideal bir seçenek haline getirir.
ng2-dragula ile Başlangıç
Angular uygulamanıza ng2-dragula‘yı entegre etmek oldukça basit bir süreçtir. İşte adım adım yapmanız gerekenler:
Ön Gereksinimler
Başlamadan önce aşağıdaki araçların sisteminizde kurulu olduğundan emin olun:
* Node.js ve npm: Angular CLI için gereklidir.
* Angular CLI: npm install -g @angular/cli komutuyla kurabilirsiniz.
* Temel Angular Bilgisi: Bileşenler, modüller, direktifler ve servisler hakkında temel bilgiye sahip olmanız önerilir.
Yeni Bir Angular Projesi Oluşturma
Eğer halihazırda bir Angular projeniz yoksa, yeni bir proje oluşturarak başlayabiliriz:
ng new my-drag-drop-app --routing false --style scss
cd my-drag-drop-app
Bu komut, my-drag-drop-app adında yeni bir Angular projesi oluşturacak ve SCSS stil dosyalarını kullanacak şekilde yapılandıracaktır.
ng2-dragula Kurulumu
Projenizin dizinine geçtikten sonra, ng2-dragula ve temel bağımlılığı olan dragula kütüphanelerini npm aracılığıyla kurun:
npm install ng2-dragula dragula --save
Bu komut, kütüphaneleri projenizin node_modules klasörüne indirecek ve package.json dosyanıza bağımlılık olarak ekleyecektir.
Modülü İçeri Aktarma
ng2-dragula‘yı kullanabilmek için, uygulamanızın ana modülüne (app.module.ts veya ilgili feature modülünüze) DragulaModule‘ü içeri aktarmanız gerekir:
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DragulaModule } from 'ng2-dragula'; // ng2-dragula modülünü import ediyoruz
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
DragulaModule.forRoot() // DragulaModule'ü forRoot() ile import ediyoruz
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
DragulaModule.forRoot() metodunu kullanmak önemlidir, çünkü bu, DragulaService‘in uygulama genelinde tek bir örneğinin (singleton) sağlanmasını garanti eder. Eğer bir feature modülünde kullanacaksanız, DragulaModule‘ü forRoot() olmadan import edebilirsiniz.
CSS Dosyalarını İçeri Aktarma
ng2-dragula‘nın varsayılan stilini kullanmak için, dragula‘nın CSS dosyasını uygulamanıza dahil etmeniz gerekir. Bunu yapmanın en yaygın yolu, angular.json dosyanızdaki styles dizisine eklemektir:
// angular.json
"styles": [
"src/styles.scss",
"node_modules/dragula/dist/dragula.min.css" // Dragula CSS'ini buraya ekliyoruz
],
Alternatif olarak, src/styles.scss (veya src/styles.css) dosyanıza doğrudan import edebilirsiniz:
/ src/styles.scss /
@import 'dragula/dist/dragula.min.css';
Bu adımları tamamladıktan sonra, ng2-dragula‘yı Angular uygulamanızda kullanmaya hazırsınız demektir.
Temel Kullanım: Tekli Sürükle ve Bırak Kapsayıcısı
ng2-dragula ile sürükle ve bırak işlevselliğini uygulamanın en basit yolu, tek bir liste içindeki öğeleri yeniden sıralamaktır.
HTML Yapısı
Bir sürükle ve bırak kapsayıcısı oluşturmak için, öğeleri içerecek HTML elementine (div, ul, ol vb.) [dragula] direktifini eklemeniz yeterlidir. Ayrıca, [(dragulaModel)] direktifi ile veri modelinizi bu kapsayıcıya bağlayabilirsiniz.
Yapılacaklar Listesi
-
{{ task }}
Burada:
* [dragula]="'my-bag'": Bu direktif, ul elementini “my-bag” adında bir Dragula grubuna dahil eder. Aynı gruptaki tüm kapsayıcılar arasında sürükle ve bırak işlemi yapılabilir. Tek bir kapsayıcı olsa bile bir grup adı vermek iyi bir pratiktir.
* [(dragulaModel)]="tasks": Bu direktif, tasks adlı dizi ile sürükle ve bırak kapsayıcısı arasındaki iki yönlü veri bağlamayı sağlar. Öğelerin sırası değiştiğinde, tasks dizisi otomatik olarak güncellenir.
ngFor="let task of tasks": Her bir görevi listede gösterir.
Bileşen Mantığı
Bileşeninizde (app.component.ts), sürükle ve bırakılacak öğeleri içeren bir dizi tanımlamanız gerekir:
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { DragulaService } from 'ng2-dragula';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'my-drag-drop-app';
tasks: string[] = ['Güneşlen', 'Kitap oku', 'Kod yaz', 'Yemek yap', 'Spor yap'];
constructor(private dragulaService: DragulaService) {
// Sürükle ve bırak olaylarını dinlemek için örnek bir kullanım
this.dragulaService.drag('my-bag').subscribe(({ el }) => {
console.log('Öğe sürükleniyor:', el);
});
this.dragulaService.drop('my-bag').subscribe(({ el, target, source, sibling }) => {
console.log('Öğe bırakıldı:', el);
console.log('Yeni liste:', this.tasks); // Veri modeli otomatik güncellenir
});
}
ngOnInit(): void {
// Bileşen yüklendiğinde yapılacaklar
}
}
Yukarıdaki kodda, tasks adında bir string dizisi tanımladık. DragulaService‘i constructor’da enjekte ederek, sürükleme (drag) ve bırakma (drop) gibi olayları dinleyebiliriz. drop olayından sonra this.tasks dizisinin güncellendiğini konsolda görebilirsiniz.
Minimal CSS
Öğelerin daha belirgin görünmesi ve sürükleme sırasında görsel ipuçları sağlamak için basit bir CSS ekleyebiliriz:
/ src/app/app.component.scss /
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
border: 1px solid #eee;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-radius: 8px;
background-color: #fff;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 25px;
}
.task-list {
list-style: none;
padding: 0;
margin: 0;
}
.task-item {
background-color: #f9f9f9;
border: 1px solid #ddd;
padding: 15px 20px;
margin-bottom: 8px;
border-radius: 5px;
cursor: grab; / Sürükleyebileceğini belirtmek için /
transition: background-color 0.2s ease;
display: flex;
align-items: center;
justify-content: space-between;
&:hover {
background-color: #f0f0f0;
}
}
/ Sürüklenen öğe için Dragula'nın sağladığı özel sınıflar /
.gu-mirror { / Sürüklenen öğenin "ayna" kopyası /
opacity: 0.8;
background-color: #e0f2f7; / Hafif mavi arka plan /
border: 1px dashed #007bff;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
border-radius: 5px;
padding: 15px 20px;
cursor: grabbing;
}
.gu-transit { / Sürüklenen öğenin orijinal yeri boşkenki hali /
opacity: 0;
}
.gu-hide { / Sürüklenen öğenin orijinali gizlenir /
display: none !important;
}
/ Bırakma alanı üzerine gelindiğinde /
.gu-over .task-item {
background-color: #e6f7ff; / Hafif mavi arka plan /
}
Bu temel kurulum ile, tasks listesindeki öğeleri sürükleyip bırakarak sıralarını değiştirebilirsiniz. ng2-dragula ve dragulaModel sayesinde, arka plandaki tasks dizisi otomatik olarak güncellenecektir.
İleri Düzey Kullanım: Çoklu Sürükle ve Bırak Kapsayıcıları
Çoğu zaman, öğeleri sadece tek bir liste içinde sıralamakla kalmayız; aynı zamanda farklı listeler veya panolar arasında da taşımak isteriz. ng2-dragula, aynı “bag” (çanta/grup) adına sahip birden fazla kapsayıcı tanımlayarak bu senaryoyu kolayca yönetmenizi sağlar.
Öğeleri Gruplama
Birden fazla kapsayıcı arasında sürükle ve bırak işlevselliği sağlamak için, tüm kapsayıcılarınıza aynı [dragula] grup adını vermeniz yeterlidir.
Yapılacaklar
- {{ task.name }}
Devam Edenler
- {{ task.name }}
Tamamlananlar
- {{ task.name }}
Burada üç farklı ul elementi var, her biri task-board adında aynı [dragula] grubuna ait. Bu sayede, bir ul‘den diğerine öğe sürükleyip bırakmak mümkün olacak. Ayrıca, tasks dizisi yerine task nesneleri kullanıyoruz, bu da gerçek dünya senaryolarına daha uygun.
Bileşen Mantığı
Bileşeninizde, her bir liste için ayrı bir dizi tanımlamanız gerekecek:
// src/app/app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { DragulaService } from 'ng2-dragula';
import { Subscription } from 'rxjs';
interface Task {
id: number;
name: string;
status: 'todo' | 'in-progress' | 'done';
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'my-drag-drop-app';
todoTasks: Task[] = [
{ id: 1, name: 'Proje dokümantasyonunu hazırla', status: 'todo' },
{ id: 2, name: 'API entegrasyonunu tamamla', status: 'todo' }
];
inProgressTasks: Task[] = [
{ id: 3, name: 'Kullanıcı arayüzünü tasarla', status: 'in-progress' }
];
doneTasks: Task[] = [
{ id: 4, name: 'Veritabanı şemasını oluştur', status: 'done' }
];
private subs = new Subscription(); // Abonelikleri yönetmek için
constructor(private dragulaService: DragulaService) {
this.subs.add(this.dragulaService.dropModel('task-board').subscribe(({ el, target, source, sourceModel, targetModel, item }) => {
console.log('Öğe bırakıldı ve model güncellendi:', item);
console.log('Kaynak Model:', sourceModel);
console.log('Hedef Model:', targetModel);
// Bu noktada, veritabanı veya API'nizi güncellemek için gerekli mantığı yazabilirsiniz.
// Örneğin, item.status'u güncelleyip bir API çağrısı yapabilirsiniz.
if (target.classList.contains('todo-list')) { // Örnek bir kontrol
item.status = 'todo';
} else if (target.classList.contains('in-progress-list')) {
item.status = 'in-progress';
} else if (target.classList.contains('done-list')) {
item.status = 'done';
}
console.log('Güncellenmiş öğe:', item);
}));
this.subs.add(this.dragulaService.removeModel('task-board').subscribe(({ el, source, sourceModel, item }) => {
console.log('Öğe kaynaktan kaldırıldı:', item);
// Öğenin kaldırıldığı durumu yönetebilirsiniz (örneğin, bir çöp kutusuna sürükleme).
}));
}
ngOnInit(): void {
// Bileşen yüklendiğinde yapılacaklar
}
ngOnDestroy(): void {
this.subs.unsubscribe(); // Bellek sızıntılarını önlemek için abonelikleri iptal et
}
}
Bu örnekte, Task arayüzü ile daha karmaşık bir veri yapısı kullandık. dragulaService.dropModel olayını dinleyerek, bir öğe bırakıldığında hem kaynak hem de hedef veri modellerine erişebiliriz. item parametresi, taşınan öğenin kendisini içerir. Bu sayede, öğenin durumunu (örneğin status özelliğini) güncelleyebilir ve bu değişikliği bir arka uç servisine bildirebilirsiniz. ngOnDestroy içinde abonelikleri temizlemek, bellek sızıntılarını önlemek için iyi bir pratiktir.
CSS Düzenlemesi
Kanban benzeri bir pano görünümü için CSS’i biraz daha geliştirebiliriz:
/ src/app/app.component.scss /
.board-container {
display: flex;
justify-content: space-around;
margin: 50px auto;
max-width: 1200px;
gap: 20px; / Sütunlar arası boşluk /
}
.board-column {
flex: 1;
min-width: 300px;
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
h3 {
text-align: center;
color: #495057;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #dee2e6;
}
}
.task-list {
list-style: none;
padding: 0;
min-height: 100px; / Boşken bile bırakma alanı olması için /
margin: 0;
}
.task-item {
background-color: #ffffff;
border: 1px solid #ced4da;
padding: 12px 15px;
margin-bottom: 10px;
border-radius: 6px;
cursor: grab;
transition: all 0.2s ease-in-out;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
&:hover {
background-color: #e9ecef;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
}
/ Dragula'nın özel sınıfları /
.gu-mirror {
opacity: 0.8;
background-color: #d1ecf1; / Hafif mavi /
border: 1px dashed #007bff;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
transform: rotate(2deg); / Hafif bir rotasyon efekti /
}
.gu-transit {
opacity: 0;
}
.gu-hide {
display: none !important;
}
.gu-unselectable {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.gu-over .task-list { / Bırakma alanı üzerine gelindiğinde /
background-color: #e2f0ff; / Daha belirgin bir mavi /
border-color: #007bff;
}
Bu CSS ile, Kanban benzeri üç sütunlu bir pano elde edersiniz ve öğeleri sürükleyip bırakarak durumlarını değiştirebilirsiniz. dragulaModel sayesinde, arka plandaki veri dizileri de otomatik olarak güncellenir.
Sürükle ve Bırak Davranışını Özelleştirme
ng2-dragula, sürükle ve bırak davranışını ihtiyaçlarınıza göre ayarlamanız için zengin bir API sunar. Bu özelleştirmeler genellikle iki ana yolla yapılır: DragulaService olaylarını dinleyerek ve grup seçeneklerini (options) ayarlayarak.
DragulaService Olayları
DragulaService, sürükle ve bırak yaşam döngüsündeki çeşitli noktalarda tetiklenen bir dizi olay sağlar. Bu olaylara abone olarak, uygulamanızın belirli durumlarda özel mantık yürütmesini sağlayabilirsiniz. En yaygın olaylar şunlardır:
* drag(bagName): Bir öğe sürüklenmeye başladığında tetiklenir.
* drop(bagName): Bir öğe geçerli bir bırakma alanına bırakıldığında tetiklenir.
* over(bagName): Sürüklenen öğe bir kapsayıcının üzerine geldiğinde tetiklenir.
* out(bagName): Sürüklenen öğe bir kapsayıcıdan çıktığında tetiklenir.
* remove(bagName): Bir öğe bir kapsayıcıdan çıkarıldığında (örneğin removeOnSpill true ise) tetiklenir.
* cancel(bagName): Sürükleme işlemi iptal edildiğinde (örneğin geçersiz bir alana bırakıldığında) tetiklenir.
* cloned(bagName): Sürüklenen öğenin kopyası oluşturulduğunda tetiklenir (yalnızca copy seçeneği true ise).
* shadow(bagName): Sürüklenen öğenin gölge (yer tutucu) öğesi taşındığında tetiklenir.
* dropModel(bagName): [(dragulaModel)] kullanılıyorsa, öğe bırakıldığında ve model güncellendiğinde tetiklenir. Bu, genellikle model güncellemelerini yakalamak için en kullanışlı olaydır.
* removeModel(bagName): [(dragulaModel)] kullanılıyorsa, öğe modelden kaldırıldığında tetiklenir.
Bu olaylara DragulaService üzerinden abone olabilirsiniz:
// app.component.ts içinde
import { DragulaService } from 'ng2-dragula';
import { Subscription } from 'rxjs';
// ... (diğer importlar)
export class AppComponent implements OnInit, OnDestroy {
// ... (diğer özellikler)
private subs = new Subscription();
constructor(private dragulaService: DragulaService) {
// Drop olayını dinleme
this.subs.add(this.dragulaService.drop('task-board').subscribe(({ el, target, source, sibling }) => {
console.log('Drop event: Bir öğe bırakıldı.');
// el: Bırakılan HTML elementi
// target: Öğenin bırakıldığı kapsayıcı HTML elementi
// source: Öğenin geldiği kapsayıcı HTML elementi
// sibling: Öğenin bırakıldığı yerdeki komşu HTML elementi (varsa)
}));
// dropModel olayını dinleme (veri modelini güncellemek için daha uygun)
this.subs.add(this.dragulaService.dropModel('task-board').subscribe(({ el, target, source, sourceModel, targetModel, item }) => {
console.log('dropModel event: Veri modeli güncellendi.');
console.log('Taşınan öğe:', item);
// item: Taşınan öğenin kendisi (veri modelindeki nesne)
// sourceModel: Kaynak liste (dizi)
// targetModel: Hedef liste (dizi)
// Örnek: Öğenin durumunu güncelle ve bir API çağrısı yap
if (target.id === 'todo-list') {
item.status = 'todo';
} else if (target.id === 'in-progress-list') {
item.status = 'in-progress';
} else if (target.id === 'done-list') {
item.status = 'done';
}
// this.taskService.updateTask(item).subscribe(); // Gerçek bir senaryoda bu şekilde kullanılabilir
}));
// drag olayını dinleme
this.subs.add(this.dragulaService.drag('task-board').subscribe(({ el, source }) => {
console.log('Drag event: Bir öğe sürükleniyor.');
// el: Sürüklenen HTML elementi
// source: Öğenin geldiği kapsayıcı HTML elementi
// Sürükleme başladığında görsel bir ipucu vermek için kullanılabilir
el.classList.add('is-dragging');
}));
this.subs.add(this.dragulaService.cancel('task-board').subscribe(({ el, container, source }) => {
console.log('Cancel event: Sürükleme iptal edildi.');
el.classList.remove('is-dragging');
}));
this.subs.add(this.dragulaService.over('task-board').subscribe(({ el, container, source }) => {
console.log('Over event: Bir kapsayıcı üzerine gelindi.');
container.classList.add('gu-over-highlight');
}));
this.subs.add(this.dragulaService.out('task-board').subscribe(({ el, container, source }) => {
console.log('Out event: Bir kapsayıcıdan çıkıldı.');
container.classList.remove('gu-over-highlight');
}));
}
ngOnDestroy(): void {
this.subs.unsubscribe(); // Abonelikleri temizle
}
}
Bu olayları dinleyerek, sürükle ve bırak işlemlerinin her aşamasında özel mantık yürütebilir, görsel geri bildirim sağlayabilir ve veri modelinizi senkronize edebilirsiniz.
Dragula Seçenekleri (Options)
ng2-dragula, dragula kütüphanesinin temel seçeneklerini destekler. Bu seçenekleri kullanarak sürükle ve bırak davranışını daha ayrıntılı bir şekilde kontrol edebilirsiniz. Seçenekleri, DragulaService.createGroup() metodunu kullanarak tanımlarsınız:
// app.component.ts içinde, constructor'da veya ngOnInit'te
import { DragulaService } from 'ng2-dragula';
export class AppComponent implements OnInit {
// ...
constructor(private dragulaService: DragulaService) {
this.dragulaService.createGroup('my-custom-bag', {
// Seçenekleri burada tanımlayın
moves: (el, source, handle, sibling) => {
// el: Sürüklenen element
// source: Kaynak kapsayıcı
// handle: Sürüklemeyi başlatan element
// sibling: Sürüklenen elementin komşusu
// Sadece belirli bir sınıfa sahip öğelerin sürüklenmesine izin ver
return handle.classList.contains('draggable-item');
},
accepts: (el, target, source, sibling) => {
// el: Sürüklenen element
// target: Hedef kapsayıcı
// source: Kaynak kapsayıcı
// sibling: Hedefteki komşu element
// Sadece 'todo' listesinden 'in-progress' listesine sürüklemeye izin ver
return source.id === 'todo-list' && target.id === 'in-progress-list';
},
copy: false, // Öğeyi sürüklerken kopyala (true) veya taşı (false)
revertOnSpill: true, // Geçersiz bir alana bırakıldığında öğeyi geri yerine koy
removeOnSpill: false, // Geçersiz bir alana bırakıldığında öğeyi tamamen kaldır
direction: 'vertical', // Sürükleme yönü: 'horizontal' veya 'vertical'
mirrorContainer: document.body, // Sürüklenen öğenin kopyasının ekleneceği kapsayıcı
ignoreInputTextSelection: true // Metin giriş alanlarındaki seçimi yok say
});
// ... (diğer olay abonelikleri)
}
ngOnInit(): void {
// HTML'de bu grubu kullanmak için:
// ...
}
}
Yaygın kullanılan bazı seçenekler ve açıklamaları:
* moves(el, source, handle, sibling) (function): Bir öğenin sürüklenip sürüklenemeyeceğini belirler. true dönerse sürüklemeye izin verilir, false dönerse engellenir. Bu fonksiyon, belirli öğeleri sürüklenemez yapmak veya sadece belirli bir “handle” (tutacak) üzerinden sürüklemeye izin vermek için çok kullanışlıdır.
* accepts(el, target, source, sibling) (function): Bir öğenin belirli bir hedef kapsayıcıya bırakılıp bırakılamayacağını belirler. true dönerse bırakmaya izin verilir. Bu, kurallı bırakma alanları oluşturmak için harikadır (örneğin, “Tamamlandı” sütununa sadece “Devam Ediyor” öğelerinin bırakılmasına izin vermek).
* copy (boolean): true olarak ayarlanırsa, sürüklenen öğe kopyalanır ve orijinali yerinde kalır. false ise öğe taşınır.
* revertOnSpill (boolean): true ise, bir öğe geçerli bir bırakma alanı dışına bırakıldığında orijinal konumuna geri döner.
* removeOnSpill (boolean): true ise, bir öğe geçerli bir bırakma alanı dışına bırakıldığında tamamen kaldırılır. Bu, bir çöp kutusu işlevi oluşturmak için kullanılabilir.
* direction (string): Sürükleme yönünü belirler: 'vertical' (varsayılan) veya 'horizontal'.
* mirrorContainer (HTMLElement): Sürüklenen öğenin “ayna” kopyasının ekleneceği DOM elementi. Varsayılan olarak document.body‘dir.
* ignoreInputTextSelection (boolean): true ise, metin giriş alanlarındaki (input, textarea) metin seçimi sürükleme işlemini başlatmaz.
Bu seçenekleri kullanarak, uygulamanızın sürükle ve bırak etkileşimlerini hassas bir şekilde kontrol edebilir ve kullanıcılarınız için özel deneyimler oluşturabilirsiniz.
Gerçek Dünya Senaryoları ve En İyi Uygulamalar
ng2-dragula‘yı gerçek dünya Angular uygulamalarına entegre ederken göz önünde bulundurmanız gereken bazı önemli noktalar ve en iyi uygulamalar vardır.
Durumu Kaydetme (Persisting State)
Sürükle ve bırak işlemleri genellikle uygulamanın durumunu değiştirir. Bu değişikliklerin kullanıcı oturumları arasında veya farklı cihazlarda kalıcı olması için, durumu bir arka uç servisine (API) veya yerel depolamaya kaydetmeniz gerekir.
dragulaService.dropModel() olayı, bu işlem için ideal bir yerdir. Bu olay tetiklendiğinde, targetModel ve item gibi parametrelere erişerek değişen veriyi alabilir ve bir HTTP isteği ile API’nize gönderebilirsiniz:
// app.component.ts içinde dropModel aboneliği
this.subs.add(this.dragulaService.dropModel('task-board').subscribe(({ el, target, source, sourceModel, targetModel, item }) => {
console.log('Öğe bırakıldı ve model güncellendi:', item);
// Öğenin yeni konumuna göre durumunu güncelle
if (target.id === 'todo-list') {
item.status = 'todo';
} else if (target.id === 'in-progress-list') {
item.status = 'in-progress';
} else if (target.id === 'done-list') {
item.status = 'done';
}
// API'ye güncelleme isteği gönder
// this.taskService.updateTask(item.id, { status: item.status, order: targetModel.indexOf(item) })
// .subscribe(
// response => console.log('Görev güncellendi:', response),
// error => console.error('Güncelleme hatası:', error)
// );
// Ayrıca, targetModel'in yeni sıralamasını da kaydetmek isteyebilirsiniz.
// targetModel.forEach((task, index) => {
// this.taskService.updateTask(task.id, { order: index }).subscribe();
// });
}));
Bu yaklaşım, kullanıcı bir öğeyi sürükleyip bıraktığında, uygulamanızın arka plandaki veritabanını veya diğer kalıcı depolama alanlarını güncelleyebilmesini sağlar.
Karmaşık Veri Yapıları
Yukarıdaki örneklerde Task arayüzü ile basit objeler kullandık. Gerçek uygulamalarda, öğeleriniz daha karmaşık iç içe geçmiş objeler içerebilir. ng2-dragula, [(dragulaModel)] direktifi sayesinde bu tür karmaşık veri yapılarıyla da sorunsuz çalışır. dropModel olayı, taşınan öğenin tüm orijinal özelliklerini içeren item parametresini sağlar.
Erişilebilirlik (Accessibility)
Sürükle ve bırak arayüzleri, fare veya dokunmatik ekran kullanıcıları için sezgisel olsa da, klavye veya yardımcı teknoloji kullanan kullanıcılar için erişilebilirlik sorunları yaratabilir. ng2-dragula varsayılan olarak klavye erişilebilirliği sağlamaz. Erişilebilir bir sürükle ve bırak deneyimi sunmak için ek çaba sarf etmeniz gerekebilir:
* Klavye Kontrolleri: Öğeleri klavye ile seçme, taşıma ve bırakma mekanizmaları ekleyin (örneğin, ok tuşları ile taşıma, Enter tuşu ile bırakma).
* ARIA Nitelikleri: Öğelere ve kapsayıcılara aria-grabbed, aria-dropeffect, aria-labelledby gibi ARIA nitelikleri ekleyerek yardımcı teknolojilere bilgi sağlayın.
* Alternatif Giriş Yolları: Sürükle ve bırak yerine, öğeleri taşımak için alternatif düğmeler veya açılır menüler sunmayı düşünün.
Performans
Çok sayıda öğe (örneğin yüzlerce veya binlerce) içeren listelerde sürükle ve bırak kullanırken performans sorunları yaşanabilir. ng2-dragula genellikle hafiftir, ancak DOM manipülasyonları büyük listelerde yavaşlayabilir.
* Sanal Kaydırma (Virtual Scrolling): @angular/cdk/scrolling gibi kütüphaneleri kullanarak sadece görünürdeki öğeleri render edin. Bu, DOM boyutunu küçülterek performansı artırır. Ancak, sanal kaydırma ile sürükle ve bırak entegrasyonu bazen karmaşık olabilir.
* Gereksiz Yeniden Render Etmeleri Önleme: OnPush değişiklik algılama stratejisini kullanarak bileşenlerinizin yalnızca giriş verileri değiştiğinde yeniden render edilmesini sağlayın.
Stil ve Görsel Geri Bildirim
Kullanıcılara sürükleme ve bırakma işlemleri hakkında net görsel geri bildirim sağlamak, kullanıcı deneyimini iyileştirir.
* Sürüklenen Öğenin Stili: gu-mirror sınıfını kullanarak sürüklenen öğenin kopyasını özelleştirin. Opaklık, gölge veya hafif bir döndürme efekti ekleyebilirsiniz.
* Kaynak Öğenin Stili: gu-transit sınıfı, sürüklenen öğenin orijinal yerini temsil eder. Genellikle bu öğenin opaklığını azaltarak veya tamamen gizleyerek kullanıcının nereye bırakacağını daha net görmesini sağlarız.
* Bırakma Alanının Stili: gu-over sınıfı, sürüklenen öğe bir kapsayıcının üzerine geldiğinde kapsayıcıya eklenir. Bu sınıfı kullanarak, bir bırakma alanının aktif olduğunu gösteren bir vurgu (örneğin, kenarlık rengi veya arka plan rengi değişikliği) ekleyebilirsiniz.
* “Handle” (Tutacak) Kullanımı: Eğer öğelerin sadece belirli bir kısmından sürüklenmesini istiyorsanız, moves seçeneğini kullanarak bir “handle” tanımlayın. Bu, özellikle karmaşık öğelerde yanlışlıkla sürüklemeyi önler.
::
{{ task.name }}
Ve moves seçeneğinde handle.classList.contains('draggable-item') kontrolünü yapın.
Sorun Giderme ve Sık Karşılaşılan Sorunlar
ng2-dragula kullanırken karşılaşabileceğiniz bazı yaygın sorunlar ve çözümleri:
* Sürükle ve Bırak Çalışmıyor:
* DragulaModule.forRoot()‘ı app.module.ts‘e veya DragulaModule‘ü ilgili feature modülüne imports dizisine eklediğinizden emin olun.
* [dragula] direktifinin doğru bir grup adıyla HTML elementine eklendiğinden emin olun.
* dragula.min.css dosyasının angular.json‘a veya ana stil dosyanıza doğru şekilde import edildiğinden emin olun.
* Konsolda hata olup olmadığını kontrol edin.
* CSS Stilleri Uygulanmıyor:
* dragula.min.css‘in doğru yoldan import edildiğinden emin olun. Yanlış yol, stillerin yüklenmemesine neden olur.
* Kendi özel stilleriniz, Dragula’nın varsayılan stillerini geçersiz kılıyor olabilir. CSS önceliğini kontrol edin.
* [(dragulaModel)] Veriyi Güncellemiyor:
* dragulaModel direktifini doğru bir şekilde iki yönlü bağlama ([()]) ile kullandığınızdan emin olun.
* Veri modelinizin (örneğin tasks dizisi) doğru bir şekilde bileşeninizde tanımlandığından emin olun.
* Eğer karmaşık objeler kullanıyorsanız, objelerin referansları yerine değerleri değiştiğinde Angular’ın değişiklik algılamasının tetiklenmesi için dropModel olayını dinleyerek manuel olarak ChangeDetectionRef.detectChanges() çağrısı yapmanız gerekebilir (ancak ng2-dragula genellikle bunu otomatik halleder).
* Olaylar Tetiklenmiyor:
* DragulaService‘i bileşeninizin constructor’ına doğru şekilde enjekte ettiğinizden emin olun.
* Olaylara abone olurken doğru grup adını kullandığınızdan emin olun (this.dragulaService.drop('my-bag').subscribe(...)).
* Aboneliklerinizi ngOnDestroy içinde temizlemeyi unutmayın (this.subs.unsubscribe()).
ng2-dragula Alternatifleri
Angular ekosisteminde sürükle ve bırak için başka seçenekler de bulunmaktadır:
* @angular/cdk/drag-drop: Angular Component Dev Kit (CDK) bünyesinde yer alan bu kütüphane, Angular’ın resmi sürükle ve bırak çözümüdür. Tamamen Angular ile entegre, güçlü ve esnektir. Özellikle Angular ekosistemine daha derinlemesine entegrasyon ve daha fazla kontrol arayanlar için iyi bir seçenektir. Ancak, kurulumu ve kullanımı ng2-dragula‘ya göre biraz daha karmaşık olabilir.
* ngx-sortablejs: SortableJS kütüphanesini Angular’a taşıyan başka bir popüler çözümdür. SortableJS, dragula gibi hafif ve esnek bir kütüphanedir. ngx-sortablejs de benzer bir kullanım kolaylığı sunar.
Ne zaman ng2-dragula‘yı seçmelisiniz?
* Hızlı ve basit bir sürükle ve bırak çözümüne ihtiyacınız varsa.
* Dragula‘nın hafifliğini ve minimal bağımlılıklarını tercih ediyorsanız.
* Listeler arasında veya içinde öğe sıralama ve taşıma gibi temel senaryolar için yeterli bir çözüm arıyorsanız.
* Özellikle karmaşık olmayan projelerde veya prototipleme aşamasında hız kazanmak istiyorsanız.
Ne zaman @angular/cdk/drag-drop‘ı seçmelisiniz?
* Angular ekosistemine daha derinlemesine entegre, resmi bir çözüme ihtiyacınız varsa.
* Sürükle ve bırak işlemleri üzerinde daha ince taneli kontrol (örneğin özel yer tutucular, karmaşık kısıtlamalar) gerekiyorsa.
* Angular Material veya CDK’nın diğer bileşenleriyle birlikte kullanıyorsanız.
* Daha büyük ve karmaşık projelerde uzun vadeli sürdürülebilirlik önceliğiniz ise.
Her iki kütüpherenin de kendine özgü avantajları vardır ve seçim, projenizin özel gereksinimlerine ve geliştirme ekibinin tercihine bağlıdır.
Sonuç
ng2-dragula, Angular uygulamalarınıza sürükle ve bırak işlevselliği eklemek için güçlü, esnek ve kullanımı kolay bir çözümdür. Basit kurulumu, [(dragulaModel)] ile veri modeline doğrudan entegrasyonu ve zengin olay/seçenek API’si sayesinde, hem temel liste sıralamalarından hem de karmaşık Kanban panosu benzeri arayüzlere kadar geniş bir yelpazede sürükle ve bırak ihtiyaçlarınızı karşılayabilir.
Bu makalede ele aldığımız adımları ve örnekleri takip ederek, Angular uygulamalarınızın kullanıcı deneyimini ng2-dragula ile önemli ölçüde zenginleştirebilirsiniz. Unutmayın, kullanıcı dostu arayüzler, uygulamanızın başarısı için kritik öneme sahiptir ve sürükle ve bırak, bu hedefe ulaşmanıza yardımcı olabilecek güçlü bir araçtır. Projenizin ihtiyaçlarına göre ng2-dragula‘nın sunduğu esnekliği kullanarak, özelleştirilmiş ve etkileşimli kullanıcı arayüzleri oluşturmaktan çekinmeyin.
