AngularJS Directive’leriyle Sağ Tık Menüsü

Yaprak Ayazoğlu —  16 Temmuz 2014 — 3 Comments

Directive’ler AngularJS’in temel taşı ve en kuvvetli özelliğidir. Bu sayede, kendi işimize yarayacak html tagler üretebiliriz. AngularJS kütüphanesiyle yazılmış kodlara baktığımız zaman ng-app, ng-repeat, ng-controller diye gördüğümüz her şey aslında birer directivedir. Şimdiye kadar ben de bu directivelerin son kullanıcısıydım. Artık kafama koydum ufak da olsa bu deryaya bir adım atacağım 🙂

Detaylı bilgi edinebilmek için bir çok kaynak okudum, teknik konuşmalar izledim ve aslında directivelerden önce linking, compiling, scope, data binding gibi kavramların da iyice sindirilmesi gerektiğini fark ettim. Bu araştırma sürecinde AngularJS’in yazarlarından Misko Hevery’nin bu video kaydını oldukça faydalı buldum. Gerek directiveler olsun, gerek AngularJS’in çalışma prensibi olsun oldukça basit bir dille ifade edilmiş. Scopelarla ilgili olarak bu günlük yazısına bir göz atmanızı tavsiye ederim. AngularJS’te tek yönlü bağlama (one way binding), iki yönlü bağlama (two-way binding), metin bağlama (text binding) diye üç tip yöntem var. Bütün bu yöntemler bu kaynakta güzel açıklanmış. Vaktiniz olursa bu kitaba da bakmanızı öneririm.

Bu kadar araştırma yaptıktan sonra artık sağ-tık directive’ini yazmaya başlayalım derim. Öncelikle, bir directive tasarlarken bu elemanın tekrar tekrar kullanılabilecek olması gerekmektedir. Peki, tekrar tekrar kullanılabilecek bir sağ-tık menüsü oluştururken nasıl bir yol izleyelim. Benim aklıma iki yöntem geldi:

  1. Sağ tıklandığı zaman gösterilip(show), DOM elemanı terk edildiği(mouseleave) zaman gizlemek (JSFiddle #1)
  2. Sağ tıklanan elemana context menünüb eklenip(append), çıkartılması(remove). (JSFiddle #2)

Eminim ki bu yöntemlerden daha iyi bir yöntem kozmozun içinde bir yerlerde mevcuttur. Ben de daha detaylı bir araştırma yapıp, bu konu üzerine daha çok emek harcayıp daha iyi bir directive yazabilirdim ama en azından birazcık kötü olsun ama bizim olsun mantığıyla kendi bu konu ile ilgili yaklaşımlarımı özetlemek istedim. Eğer aklınıza başka yöntemler, mevcut yöntemlere eklenmelik güzellikler geliyorsa yorumlarınızı bekliyorum 🙂

Directive Denilince Aklıma Gelenler

Yöntemleri detaylı olarak anlatmadan önce aslında kendi örneklerim üzerinden Directivelerin çalışma mantığını anlatmak istiyorum. 1. yaklaşımın HTML kodundan başlayalım. Aşağıdaki kodda, id gibi görmeye alışkın olduğumuz attributelar haricinde ng-controller, ng-contextmenu, ng-show gibi tuhaf attributelar da görüyoruz. Aslında bunların hepsi birer AngularJS Directive’i.

Bu Directive’ler Nasıl İşer Hale Geliyor ki?

  • AngularJS İlk Yüklemesi

Websitemizin index.html dosyası içinde  <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.18/angular.min.js"> </script>  satırını yazığımız andan itibaren aslında AngularJS çatısı artık bizim elimiz kolumuz. Tarayıcı yüklediği Javascript dosyasını aynı zamanda çalıştırıyor. Biz de AngularJS koduna baktığımız zaman en son satırında kendisini çağırdığını görüyoruz.

Dolayısıyla, tarayıcının Javascript işleyen birimi bu kodu ekler eklemez çalıştırdı. Dikkat ederseniz malum fonksiyonun girdi parametrelerden biri de güncel pencerenin HTML’inin document objesi. Angular çalıştığı zaman, bu document objesine aynı zamanda DOMContextLoaded etkinliğini(event) ekliyor. Yani HTML tamamen ayrıştırılıp da DOM objesi yaratıldığı zaman, Angular kodu geri çağrıyı(callback) yakalayacak, DOM objesinde ki bütün Directiveleri işleyecek ve çalışır hale getirecek. Tabii ki ilk yükleme kısmı için söylenecek çok fazla şey var ama burada bilinmesi gereken şey, bütün DOM yüklendikten sonra AngularJS’in kendisiyle ilgili bütün directiveleri işliyor olduğu.

Peki ilk yüklemeden sonra neler oluyor?

  • Directivelerin Anası ng-app

AngularJS tırım tırım Directivelerin anasını yani  ng-app i arıyor. ng-app yoksa yemek de yok ve sadece bir tane ng-app olsun istiyor. İlk aşkı bulduktan sonra diğerlerini de tınlamıyor. Bundan sonra da diğer Directiveler işler hale getiriliyor. Yani bizim koddaki ng-controller, ng-contextmenu, ng-show artık bir şey ifade ediyor.

Benim yazdığım directive ng-contextmenu nasıl çalışıyor ki?

Peki, Angular ng-contextmenuyü işledikten sonra neler oldu? JSFiddle ve benim post arasında mekik dokumayın diye kodu ayağiniza getirdim:

Hizmette sınır yok:)

Dikkat ederseniz en başta bir contextMenu objesi yarattım(var contextMenu = {}). Sonra bu objeye restrict, scope ve link elemanları tanımladım. En son olarak bu objeyi döndüm. Döndüğüm obje AngularJS’in bir directive tanımlamak için gerekli olan bilgileri içeriyor.

Bu Restrict ne ki?

restrict bu Directivei eleman içinde nasıl tanımlayacağımı ifade ediyor. Sadece “A”, “E” yada “C” harflerini atayabiliyorum. A dediğimiz attribute name, E dediğimiz element name, C dediğimiz de class-name olur. Yani A dediğimiz zaman <div hanimis-benim-directivim></div>  gibi kullanıyoruz. E dediğimiz zaman kendi HTML elemanımı tanımlamış oluyorum ve <hanimis-benim-directivim></hanimis-benim-directivim> gibi kullanabiliyorum. C dediğim zaman da kendi classolarak tanımlayabiliyorum. Mesela, <div class='hanimis-benim-directivim'></div>. Genellikle de AE opsiyonları kullanılıyor.

Peki ya Scope?

Scope ile directive’in bulunduğu scope’taki hangi elemanlara erişimi olduğunu ifade edebiliyoruz. Ben scope içinde sadece isVisible="=" gibi bir ifade kullanmışım. Yani diyorum ki: “Benim işim sadece  isVisible elemanıyla başka hiç bir şey umrumda bile değil!” Burada = işaretiyle iki yönlü bağlama yapmış oluyoruz. Yani scope içinde isVisible elemanında yapılan herhangi bir değişiklik Directive‘in içinde de algılandığı gibi, Directive‘in içinde yaptığımız değişiklik de scope’da da algılanıyor. Bunun dışında iki farklı binding türü daha var. Artık bunların açıklamalarını da başka bir yazıda konuşuruz.

Link?

Link fonksiyonu, Directive DOM elemanına bağlanırken çalıştırılıyor. AngularJS, directiveleri bulup da işlerken directive’in tanımlı olduğu DOM elemanının başını bu link fonksiyonu ile bağlıyor.

Dikkat edilirse, bizim örneğimizde bu fonksiyona lScope, lElem, lAttr gibi girdiler var. lElem bu directive’in bağlandığı eleman oluyor. lScope ise bulunduğu scope oluyor. Dikkat ederseniz link fonksiyonun da elemana contextmenu ve mouseleave aksiyonlarını ekledim. Yani ilgili elemana sağ tıklandığı zaman contextmenu açılsın. Mouse imleci elemandan ayrıldığı zaman da bu menü kapansın istiyorum.

Sağ tık menüsü haricinde bir torba laf ettik. Artık konunun anasına dönelim:

Birinci Yöntem:

İlk yöntem aslında tekrar tekrar kullanılabilme prensibine daha yakın ancak bu durumda da sağ tıkladığım elemandan ayrılıp, menü üzerinde gezmeye karar verirsem sağ tık menüsü kayboluyor. Çünkü burada context-menu en başta HTML içinde yaratılmış. Elemana sağ tıklanması durumunda ise sadece görünürlük durumunu değiştiriliyor. Oysa ki sağ tıklanılan elemanın içine append edilmesi gerekiyor ki context-menu üzerinde gezebilelim. Belki context-div sağ tıklanan elemana dinamik olarak eklenebilir ama bu yöntem nedense pek şık gelmedi.

İkinci Yöntem:

Bu yüzden bir de ekleme & çıkarma yöntemini de denemeye karar verdim. Bu yöntemde, sağ tık menüsünü HTML’de tanımlamak yerine Javascript kodumda($scope.myContextDiv) tanımladım. Şimdi bu durumda

  1. Bunu da context iki tane önemli görevi var: HTML’de bu context-menunün kullanılmasını istediğimiz elemanlara attribute olarak eklememiz gerekiyor. Benim örneğimdeki HTML’deki  <div context-menu="myContextDiv"> Right Click On the Item!</div> kod parçacığında da görüldüğü gibi context-menu attribute’una scope’ta JS dosyasında yaratmış olduğum(myContextDiv) sağ tık menüsünü yazdım. Bu şekilde sağ tık menüsünü bu elemana bağlamış oldum.
  2. Sağ tıklanması durumunda bu context-menüyü ilgili elemana şu şekilde append ettim:  lElem.append( $compile( lScope[ lAttr.contextMenu ])(lScope) ); Dikkat edersiniz ki elemanı append ederken önce compile ediyorum. AngularJS ilk yüklenme esnasında bütün directiveleri gezip bunları işler hale getiriyordu. Peki biz ilk yüklemeden sonra içinde Directiveler barındıran bir kodu DOM’a yüklersek ne olacak? Tabii ki çalışmayacak! Dolayısıyla, ilk açılışta AngularJS’in yaptığını bizim elle yapmamız gerekiyor. İşte $compile fonksiyonu tam da bu işe yarıyor.
  3. İmlecin DOM elemanından ayrılması durumunda da sağ tık menüsü, bu elemandan çıkarılıyor.

Eğer sağ tık menüsündeki seçeneklerin çalışıp çalışmadığını görnek de istiyorsanız, tarayıcınızın geliştirici arayüzünü açıp, konsol loglarına bakabilirsiniz.

Yöntemler, kodlama ile ilgilli herhangi bir öneriniz, düzeltmeniz varsa yorum yazmaktan hiç çekinmeyin 🙂

For English version refer to here.

Yaprak Ayazoğlu

Posts

3 responses to AngularJS Directive’leriyle Sağ Tık Menüsü

  1. Yazı için teşekkürler, gayet açıklayıcı olmuş. AngularJS hakkında türkçe olarak makale kıtlığı çekerken bu iyi geldi 🙂

  2. Güzel ve Eğlenceli bir yazı olmuş teşekkürler

  3. Güzel bir Türkçe kaynak.

Yorum yapmak için