#205 Unobtrusive Javascript
- Download:
- source codeProject Files in Zip (163 KB)
- mp4Full Size H.264 Video (18.8 MB)
- m4vSmaller H.264 Video (13.4 MB)
- webmFull Size VP8 Video (35 MB)
- ogvFull Size Theora Video (25.6 MB)
Rails 3 özelliklerini incelediğimiz serinin bu bölümünde unobstrusive Javascript'in kullanımını inceleyeceğiz. Unobtrusive Javascript ile web uygulamasında javascript kod ile içeriği birbirinden CSS de olduğu gibi ayrılır. Unobtrusive Javascript'i Rails'de kullanmaya başlamadan önce basit bir HTML döküman içinde kullanalım.
Aşağıdaki ekran görüntüsü üzerinde bir link olan bir web sayfasını gösteriyor. Link tıklandığında Javascript alarm popup açılır ve "Hello world!" mesajı verir.
Bu sayfanın HTML kodu şuna benzer:
<!DOCTYPE html> <html> <head> <title>UJS Example</title> </head> <body> <h1><a href="#" onclick="alert('Hello world!'); return false;">Click Here</a></h1> </body> </html>
Sayfada bir linkimiz var ve onclick özelliğine de bir Javascript kod yazılmış. Script HTML içinde birleşik olduğundan unobtrusive değildir ve iyi bir şey değildir, çünkü içerik ve kod birbirine karışıyor. 1990'ların sayfalarında CSS olmadığı için <font> tag'i ile yazı görünümü değiştirilirdi, sayfada yüzlerce bunlardan olurdu. Tarayıcılar CSS kullanmaya başlayınca stil bilgileri stylesheet dosyalarında toplanmaya başladı ve hem değişiklik yapılması kolaylaştı hem daha anlaşılır oldu.
Aynısı Javascript'e de uygulanabilir. Küçük küçük Javascript kod parçaları HTML tagların özellikleri içinde yazılırsa değişiklik yapmak zorlaşır. Javascript'i ayrı bir dosyaya koyunca tekrarlayan kodlar azalır, düzenlemeler kolaylaşır ve karmaşık uygulamaları üretmek ve debug kolaylaşır.
Örneğimizi nasıl unobtrusive yapacağız? Ana adım, onclick özelliğindeki Javascript'i ayrı bir dosyaya taşımak ve bunun için bir Javascript framework kullanacağız. Burada JQuery ile elemanların olaylarını algılayacağız. Önce sayfanın değişmişini bir görelim sonra açıklayalım.
<!DOCTYPE html> <html> <head> <title>UJS Example</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript" charset="UTF-8"></script> <script type="text/javascript"charset="UTF-8"> $(function () { $('#alert').click(function () { alert('Hello, world!'); return false; }) }); </script> </head> <body> <h1><a href="#" id="alert">Click Here</a></h1> </body> </html>
İlk Dikkat çeken nokta Javascript'i daha önce söylediğimiz gibi ayrı bir dosyaya çıkarmadık. Bunu sadece burda gösterim için yaptık.
Body içindeki elemanın onclick
özelliğini kaldırıp ona bir id
verdik. Bu sayede hedefe JQuery üzerinden bir bağlantı yapabiliriz.
Daha sonra head bölümünde JQuery kütüphanesini yüklüyoruz ve bunun
altında dış dosyaya koyacağımız script görünüyor. Script JQuery'nin $
fonksiyonunu bir fonksiyon argümanı ile çağırıyor. Argümandaki bu
fonksiyon sayfa DOM yüklemesinde çağrılır. Bu fonksiyon JQuery ile
elemanı id'sinden bulur ve click olayına bir fonksiyon bağlar. Bağlanan
fonksiyon ise bir alert ile mesaj verir ve geriye false dönerek link
bağlantısına gidilmesini engeller.
Eğer sayfayı şimdi tazelersek aynen bir öncekinin yaptığını yapacaktır. Tıklanınca bir alarm verecektir.
Burda çok iş yapmamıza rağmen karşılığında pek bir şey elde etmedik. Burada Unobtrusive Javascript avantajlarını pek göremedik tek satır Javascript yerine 6 satır oldu. Bu örnek sadece Unobtrusive Javascript'in mantığını anlatmak için yapıldı. Unobtrusive Javascript'in avantajı sayfadaki Javascript sayısı arttıkça ortaya çıkmaya başlar. Tüm Javascript kodlarını ayrı bir dosyada tutmak sayesinde tekrarlamaların önüne geçilmeye başlanır.
Bu yaklaşımın bir sorunu var. JavaScript genellikle statik bir dosyaya yazılıyor. Bu durumda satır içine yazmadığımıza göre nasıl server tarafı dinamik JavaScript ekleyebileceğiz?
HTML 5 ile birlikte artık ısmarlama verileri ilgili eleman tag içine
özellik olarak koyabiliyoruz. Bunlar aynı diğer özellikler gibi
yazılıyor, tek farkları data-
ile başlamaları. Mesela
tıklanınca verilecek mesajı özellikte saklamak için şöyle yazın:
<a href="#" id="alert" data-message="Hello from UJS">Click Here</a><br>
JavaScript'de alert satırını mesajı bu yeni özellikten alacak şekilde değiştirelim:
$(function () { $('#alert').click(function () { alert(this.getAttribute('data-message')); return false; }) });
Eğer sayfayı tazelersek linki tıklayınca özeliikten gelen mesajı alabiliriz.
Rails 3 Data Özelliklerini Nasıl Kullanır
Rails 3 bu ısmarlama data özellikler ile veriyi JavaScript'e gönderir. Şimdi bunun bir Rails 3 uygulamasında nasıl olacağını inceleyelim. Uygulamamız içinde arama yapılabilecek ürünlerin listesi bulunan basit bir e-alışveriş uygulaması. Ayrıca ürünü düzenlemek ve silmek için de linkler var , ama silmek için olan çalışmıyor gibi.
Bu Rails 3 uygulamalarında sık karşılaşılan bir sorundur. Eğer eski bir versiyon uygulamayı upgrade ediyorsanız, JavaScript'lerin çalışmaması çok karşılaşılan bir durumdur.
Görsel dosyasındaki kod "Destroy" linkini standart link_to metodunu
kullanarak üretir. :confirm opsiyonu ile bir JavaScript confirm
alarmı üretilir ve :method
opsiyonu da :delete
olarak ayarlanmıştır. Böylece link tıklanınca GET yerine DELETE
metoduna çağrı yapılacaktır.
<%= link_to "Destroy", @product, :confirm => "Are you sure?", :method => :delete %><br>
Burada enteresan olan bu kodun ürettiği HTML kod:
<a href="/products/8" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a><br>
Rails 2'de destroy link oluşturmak için link_to
kullanınca
bir sürü JavaScript ile confirm
diyaloğu oluşturuluyor ve bir form ile DELETE ya da PUT çağrısı
yapılıyordu. Rails 3 koduna bakarsanız daha temiz ve daha önce
gördüğümüz HTML 5 data özelliklerini kullanıyor. Onay mesajını içeren
bir data-confirm
ve metodu saklayan bir data-method
özelliği.
Linkin çalışmama sebebi head bölümünde JavaScript fonksiyon tanımlanmadığı için , standart bir GET çağrısı yapılacak ve silme işlemi DELETE çağrılmadığı için gerçekleşmeyecektir.
Bunu düzeltmek için uygulamanın layout dosyasına aşağıdaki iki satırı eklemeliyiz:
<%= javascript_include_tag :defaults %><br> <%= csrf_meta_tag %><br>
Yukardaki ilk satır tanıdık gelecektir. Bir Rails uygulaması için standart JavaScript dosyaları yüklüyor. İkinci satır DELETE işlemlerini gerçekleştirmek için gerekli yetkilendirme bilgilerini taşıyan iki metatag üretir. Değişim sonrası sayfayı tazelersek üretilen iki satırı göreceğiz.
<script src="/javascripts/prototype.js?1268677667" type="text/javascript"></script> <script src="/javascripts/effects.js?1268677667" type="text/javascript"></script> <script src="/javascripts/dragdrop.js?1268677667" type="text/javascript"></script> <script src="/javascripts/controls.js?1268677667" type="text/javascript"></script> <script src="/javascripts/rails.js?1268677667" type="text/javascript"></script> <script src="/javascripts/application.js?1268677667" type="text/javascript"></script> <meta name="csrf-param" content="authenticity_token"/> <meta name="csrf-token" content="9ImdFvbeW7ih9oKqBDQ3O889q/hJ1q5uajpT4DFDAoA="/>
Şimdi sayfada tüm JavaScript dosyalara erişim ve cross-site saldırılara karşı tüm önlemler mevcuttur. Böylece PUT ve DELETE çağrılarının bir hacker tarafından değil doğru kişiden geldiğinden emin olabiliriz.
Bu iki satırla beraber bizim destroy linkimiz beklendiği gibi çalışacaktır.
Bir Arama Formuna AJAX Eklemek
Sonra index sayfasındaki arama formunu değiştirerek GET çağrısı
yerine AJAX kullanmasını sağlıyacağız. Index
görselindeki form için kod
aşağıda gösteriliyor:
<% title "Products" %> <% form_tag products_path, :method => :get do %> <p> <%= text_field_tag :search, params[:search] %> <%= submit_tag "Search", :name => nil %> </p> <% end %> <div id="products"> <%= render @products %> </div> <p><%= link_to "New Product", new_product_path %></p>
Burada kullanılan form episode 37.
de gösterilen teknikle arama yapmak içindir. Rails'in önceki
versiyonlarında formun AJAX ile çalışması için form_tag
ile
form_remote_tag
değiştirilirdi. Bu metod şimdi
sakınmaya çalıştığımız satır içi JavaScript'ten bir sürü üretecektir.
Rails 3 ile beraber birçok remote helper metod artık kullanılamaz oldu. Eğer kullanmak istersek Prototype Legacy Helper plugin install etmeliyiz.
Bunu form_remote_tag
ile yapmak yerine yine form_tag
,
kullanıyoruz ama yeni bir parametre ile :remote
.
<% form_tag products_path, :method => :get, :remote => true do %> <p> <%= text_field_tag :search, params[:search] %> <%= submit_tag "Search", :name => nil %> </p><br><% end %>
Bu :remote
parametresi ayrıca link_to
,
button_to
ve form_for
gibi diğer helper metodlarla
da kullanılabilir. Eğer sayfayı tazeleyip kaynağı incelersek yeni form kodumuzun
nasıl çalıştığını görürüz.
<form action="/products" data-remote="true" method="get"> <p> <input id="search" name="search" type="text" /> <input type="submit" value="Search" /> </p><br></form>
Form elemanı aynı ama bu sefer yeni bir data-remote
özelliği var. Satır içi JavaScript yok, yeni özellik rails.js
içindeki JavaScript'e formun AJAX yoluyla gönderildiğini anlatmaya
yeter.
Sırada AJAX çağrısını işleyecek kodu yazmak var. Ürünlerin listesi
bir div
içersinde bulunuyor. Bu div
içeriğini update ederek sadece aranan ürünleri gösterebiliriz.
Form ProductController
’in
index
eylemine gönderiliyor ve tüm ihtiyacımız olan
JavaScript isteklerini işleyecek bir index.js.erb
dosyası eklemek.
Bu kalıp dosyaya tarayıcıya dönüldüğünde çalışmasını beklediğimiz herhangi bir JavaScript kodu yazabiliriz. Yeni kalıp dosyamızdaki kod div içeriğini sonuca göre update edecek.
$("products").update("<%= escape_javascript(render(@products))%>");<br>
Sayfayı tazelediğimizde ve formun gönderme düğmesine tıkladığımızda arama AJAX çağrısı ile yapılacak ve URL değişmeyecektir.
İşte böyle, Rails 3 ile unobtrusive JavaScript sadece :remote parametresi kullanarak yapılıyor. AJAX çağrısına bir server bir JavaScript ile dönüş yapıyor ve o da div içeriğini değiştiriyor.
Frameworkları Değiştirmek
Bu bölümü uygulamamızın kullandığı JavaScript Framework'u nasıl değiştirceğimizi göstererek kapatacağız. Uygulamamız şu anda Prototype kullanıyor, bu Rails'in parçası olarak geliyor , fakat JQuery kullanmak istiyorsak ne olacak?
İlkönce uygulama layout'da yer alan şu satırı değiştirmeliyiz.
<%= javascript_include_tag :defaults %><br>
şöyle değiştirin:
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", "jquery.rails.js" %><br>
Listedeki ilk dosya Google üzerinden JQuery'nin son versiyonu. Bu tek
başına yeterli değil rails.js
dosyasının JQuery eşleniğini
de yüklememiz gerekiyor, yoksa unobtrusive uygulamalarımız çalışmaz. Bu
dosyanın orjinalini jquery-ujs project on Github
adresinde bulabilirsiniz. Bu projede indirip projemizde kullanabileceğimiz
bir rails.js
dosyası vardır. Bu dosyayı indirip uygulamamızın
/public/javascripts
klasörüne koyalım ve adını jquery.rails.js
yapalım ve bu dosya uygulamamızın Rails tarafından kullanılan unobtrusive
JavaScript işlerini gerçekleştirir.
Şimdi daha önce uygulamamıza bizim eklediğimiz Prototype'a göre olan
JavaScript'imizi JQuery'ye uygun olacak şekilde değitirmek zorundayız.
Daha önce ürettiğimiz index.js.erb
dosyasında küçük bir değişiklik yapacağız. Div seçicisi olarak products
yerine #products
kullanarak ve Prototype'ın update
metodu yerine JQuery eşleniği olan html
kullanarak.
$("#products").html("<%= escape_javascript(render(@products))%>");<br>
Uygulamamız artık kaputun altında Prototype yerine JQuery kullanıyor.
Estetiğin Kaybolması
Eğer bir kullanıcı JavaScript izin verilmeyen bir tarayıcı
kullanarak uygulamamızı açarsa biraz estetik kaybı olacaktır. Buton
normal bir GET çağrısı yapacaktır. Ürün silme linki de çalışmayacaktır.
Bu genel bir problemdir, HTML linkleri sadece GET çağrıları yapabilir,
bu yüzden Rails DELETE için biraz JavaScript kullanır. Bunun önüne
geçmek için bir yöntem button_to
kullanmaktır ama çirkin görünecektir, bu yüzden link olarak kalmalı. Tercih
edilebilecek bir teknik olarak episode
77 de JavaScript izin verilmeyen tarayıcılarda silme işlemi için
başka sayfa açan örnek var.