Önceki bölümü okumadan buraya geldiyseniz hiç kasmayın geri dönün
- ORTA SEVİYE BİR RAİLS TUTORIAL - 1 (Kurulum, ilk stiller)
- ORTA SEVİYE BİR RAİLS TUTORIAL - 2 (Kullanıcı girişi - Bootstrap CSS)
- ORTA SEVİYE BİR RAİLS TUTORIAL - 3 (Blog altyapısı)
- ORTA SEVİYE BİR RAİLS TUTORIAL - 4 (JavaScript stiller)
Tasarım Değişikliği
Bu yazıda kolleksiyon sayfalarında gönderiler için başka bir stil deneyeceğiz. Ana sayfada kart tasarımını kullanmıştık. Kolleksiyonlarda liste tasarımı kullanalım. Böylece kullanıcı çok daha fazla gönderiyi görebilir ve içlerinde daha etkili gezinebilir.
Görsellerin olduğu
posts klasöründe post adında bir alt klasör tanımlayalım ve içne _home_page.html.erb adında bir parça görsel dosyası tanımlayalım.
Daha önce yazdığımız
_post.html.erb parça görselindeki kodu kesip buraya yapıştıralım.
app/views/posts/post/_home_page.html.erb
<div class="col-sm-3 single-post-card" id=<%= post_path(post.id) %>> <div class="card"> <div class="card-block"> <h4 class="post-text"> <%= truncate( post.title, :length => 60 ) %> </h4> <div class="post-content"> <div class="posted-by">Gönderen <%= post.user.name %></div> <h3><%= post.title %></h3> <p><%= post.content %></p> <%= link_to "İlgileniyorum", post_path( post.id ), class: "interested" %> </div> </div> </div><!-- card --> </div><!-- col-sm-3 -->
Şimdi içini boşalttığımız
_post.html.erb dosyası içine şu satırı yazalım:
app/views/posts/_post.html.erb
<%= render post_format_partial_path, post: post %>
Burada
post_format_partisl_path yardımcı metodunu çağırıyoruz. Bu metod bulunulan sayfaya göre hangi parça görsel ile gönderilerin yayınlanacağını belirleyecek. Eğer ana sayfa gösteriliyorsa az evvel _home_page.html.erb içine taşıdığımız görseli gösterecek. Şimdi neden görseli taşıdığımız anlaşıldı.
Kolleksiyon sayfasındaki görsel için
view/posts/post/ klasörü içinde _branch_page.html.erb dosyasını tanımlayalım ve içine şu kodu yazalım:
app/views/posts/post/_branch_page.html.erb
<div class="single-post-list" id=<%= post_path(post.id) %>> <%= truncate(post.title, :length => 60) %> <div class="post-content"> <div class="posted-by">Gönderen <%= post.user.name %></div> <h3><%= post.title %></h3> <p><%= post.content %></p> <%= link_to "İlgimi çekti", post_path(post.id), class: "interested" %> </div> </div>
Şimdi
posts_helper.rb içinde post_format_partial_path metodunu tanımlayalım:
app/helpers/posts_helper.rb
...
def post_format_partial_path
current_page?(root_path) ? "posts/post/home_page" : "posts/post/branch_page"
end
Burada bir eksik var, ana sayfaya gidersek bize
post_format_partial_path metodunu bulamadığını söyleyecek çünkü ana sayfa PagesController içindeyken bu metodu başka kontrolör içinde tanımlandığı için göremez. O zaman ana yardımcı sınıfı olan ApplicationHelper içinde PostsHelper sınıfını da dahil edelim.
app/helpers/application_helper.rb
module ApplicationHelper include NavigationHelper include PostsHelper end
CSS
Kolleksiyon sayfalarındaki gönderilerin stili için bir CSS dosyası hazırlayalım. Stillere ait
Koddanda anlaşıldığı üzere
posts klasöründe branch_page.scss dosyasını tanımlayalım.
app/assets/stylesheets/partials/posts/branch_page.scss
.single-post-list { min-height: 45px; max-height: 45px; padding: 10px 20px 10px 0; margin: 0 10px; border-bottom: solid 3px rgba(0,0,0, 0.05); border-bottom-right-radius: 10%; transition: border-color 0.1s; overflow: hidden; &:hover { cursor: pointer; } } .page-title { margin: 30px 0; text-align: center; background-color: white !important; font-weight: bold; a { color: black; } a:hover { text-decoration: underline; } } .categories-list { margin: 10px 0; padding: 0; } .category-item { display: inline-block; margin: 15px 0; a { font-size: 16px; font-size: 1.6rem; color: rgba(0,0,0, 0.7); border: solid 2px rgba(0,0,0, 0.4); border-radius: 8%; padding: 10px; } a:hover, .selected-item { background-color: $navbarColor; color: white; border: solid 2px white; border-radius: 0; } } .new-post-button-parent { text-align: right; span { font-size: 12px; font-size: 1.2rem; } } .new-post-button { display: inline-block; background: $navbarColor; color: white; padding: 8px; border-radius: 10px; font-weight: bold; border: solid 2px $navbarColor; margin: 10px 0; &:hover, &:active { background: white; color: black; } } .login-branch { margin: 10px 0; } .login-button-branch { padding: 5px 10px; border-radius: 10px; &:hover, &:active, &:visited, &:link { color: white; } } .branch-main-content { background: white; height: calc(100vh - 50px); } #feed { background-color: white; }
Size tavsiyem bu CSS satırlarının herbirinin ne işe yaradığını takip edin.
Bir de
base/default.scss dosyasına şunları ekleyelim:
app/assets/stylesheets/base/default.scss
... .login-button, .sign-up-button { background-color: $navbarColor; color: white !important; }
Küçük ekranlarla ilgili olarak düzenleme için de
mobile.scss dosyasına şunları ekleyelim.
app/assets/stylesheets/responsive/mobile.scss
... @media screen and (max-width: 550px) { .page-title { font-size: 20px; font-size: 2rem; } .new-post-button-parent { text-align: center; span { display: none !important; } } .post-button { padding: 5px; } .category-item { a { padding: 5px; } } } @media screen and (max-width: 767px) { .single-post-list { min-height: 65px; max-height: 65px; padding: 10px 0; } }
Arama Kutusu
Kolleksiyon sayfalarında sadece tüm listeyi göstermek değil kullanıcıya gönderiler içinde arama yapma kabiliyeti de vermek istiyoruz.
_branch.html.erb görsel kalıp dosyasında categories satırı üzerine şunları ekleyelim:
app/views/posts_branch.html.erb
...
<div class="row">
<%= render "posts/branch/search_form",
branch: branch,
search_placeholder: search_placeholder %>
</div>
...
Koddanda anlaşıldığı üzere
branch klasörü içinde _search_form.html.erb adında bir dosya tanımlayacağız.
app/views/posts/branch/_search_form.html.erb
<div class="col-sm-12"> <%= form_tag(send("#{branch}_posts_path"), :method => "get", id: "search-form") do %> <i class="fa fa-search" aria-hidden="true"></i> <%= text_field_tag :search, params[:search], placeholder: search_placeholder, class: "form-control" %> <%= render category_field_partial_path %> <% end %> </div>
Burada
Anlaşılacağı üzere
Arama kutusuna biraz stil vermek için
Kolleksiyon sayfalarında arama kutusu böyle görünecek:
Formumuzda görüntü var da icraat yok. Arama özelliği kazanmak için bazı gem’ler yükleyebiliriz ama bizim verilerimiz çok karmaşık değil. Bu yüzden
Önce
Bu kodun çalışmasından emin olmak için rails console kullanabiliriz. Konsolda
Not : Windows’ta başıma geldi Linux’ta olur mu bilmem ama, eğer Rails konsol utf8 temelli bir hatadan dolayı çalışmamazlık ederse bilgisayarınızın c:\Users<user_name>.irb_history adında bir dosyası var onun içini silin. Bu dosya geçmişteki irb çalışmalarınızda girdiklerinizi kaydeder ve eğer Türkçe karakter girmişseniz ondan dolayı Rails konsol utf8 hatası veriyor.
Şimdi arama kutumuzu çalıştırmak için bir şeyler yapalım. Hatırlar mısınız
Daha önce lojiğin görseller içinde olmasının tercih edilmediğinden bahsetmiştik. Bu arama kodlarının da kontrolör içinde olması pek yakışık almadı. Önümüzdeki bölümlerde bu kodları düzenleyeceğiz.
Yukarıdaki kodda gördüğümüz gibi kullanıcının yaptığı olası girdilere göre değişik kapsamlar kullanılarak
Not PostgreSQL kullanırken search sorgusunda ya LIKE yerine ILIKE kullanmak ya da lower(title) şeklinde aranan sütunu da küçük harfe çevirmek gerekebilir.
Yukarıdaki kodda joins metodu bağlantılı tablolardan kayıtları toplamak için kullanılır. Ayrıca girilen stringe göre kayıtlarda basit bir arama SQL satırı da var.
Eğer şimdi server’ı yeniden başlatırsak arama kutusunun çalıştığını görürüz. Ayrıca kolleksiyonların kategorilerine ait seçme butonları da çalışmaya başladı.
AJAX çağrısı ve koşullarını oluşturarak başlayalım. Kullanıcı aşağı scroll ederek belli bir noktayı geçtiğinde AJAX çağrısı ateşlenir.
Bu koda göre bir
Yeni yardımcı metodumuz
Burada
Karşı gelen parça dosyaları tanımlayalım:
Artık herhangi bir kolleksiyon sayfasında aşağı doğru kayarsak sonraki sayfaya ait kayıtların otomatik olarak geldiğini görürüz. Bu pagination menüyü görmeye artık ihtiyaç kalmadı. Onu da görünmez yapalım.
Bu bölüm de bu kadar. Mola verelim. Sonraki bölümde tekrar ana sayfaya dönüp bazı değişiklikler yapacağız. Görüşmek umuduyla , kalın sağlıcakla..
Sırada şunlar var:
send metodu ile bulunulan kolleksiyona bağlı olan PostsController aksiyonuna yönlendiriyoruz. Ayrıca eğer bir kategori seçiliyse onu da extra parametre olarak göndereceğiz. Kategori seçiliyse sadece o kategoriden arama sonuçları gelmeli.
category_field_partial_path yardımcı metodunu posts_helper.rb içine ekleyelim:
app/helpers/posts_helper.rb
...
def category_field_partial_path
if params[:category].present?
"posts/branch/search_form/category_field"
else
"shared/empty_partial"
end
end
Anlaşılacağı üzere
posts/branch/search_form alt klasörünü tanımlayacağız ve içine _category_field.html.erb parça görsel dosyasını koyacağız.
app/views/posts/branch/search_form/_category_field.html.erb
<%= hidden_field_tag :category, params[:category] %>
Arama kutusuna biraz stil vermek için
branch_page.scss içine biraz CSS ekleyelim:
app/assets/stylesheets/partials/posts/branch_page.scss
... .fa-search { position: absolute; bottom: 14px; left: 10px; width: 20px; height: 10px; } #search-form { position: relative; input { border: solid 2px rgba(0,0,0, 0.2); border-radius: 10px; box-shadow: none; outline: 0; } input:focus { border: solid 2px rgba(0,0,0, 0.35); } input#search { padding: 15px; width: 100%; height: 20px; margin: 10px 0; padding-left: 30px; } }
Kolleksiyon sayfalarında arama kutusu böyle görünecek:
Formumuzda görüntü var da icraat yok. Arama özelliği kazanmak için bazı gem’ler yükleyebiliriz ama bizim verilerimiz çok karmaşık değil. Bu yüzden
Post modeli içinde scope kullanmak bize yeterli olacak.
Post modeli içinde değişik kapsamlar tanımlayarak başlayalım. Isınmak için post.rb model dosyasında default_scope tanımlayalım. Bu kapsam tanımıyla gönderileri tarihlerine göre azalan sıralama yaparak en üste en yeni gönderinin gelmesini sağlayalım.
app/models/post.rb
class Post < ApplicationRecord belongs_to :user belongs_to :category default_scope -> { includes(:user).order(created_at: :desc) } end
Bu kodun çalışmasından emin olmak için rails console kullanabiliriz. Konsolda
rails console
girdiğimizde karşımıza uygulamamıza özel bir irb uygulaması açılır. OradaPost.limit(5)
girdiğimizde en üstteki 5 tane gönderi listelenecektir. Eğer default_scope değiştirmemiş olsak zaman olarak ilk girilen 5 gönderi gelir. Ama yukarıda bahsi geçen satırı modele ekleyerek default_scope değiştirdikten sonra en son girilen 5 gönderi gelecektir. Şu anda uygulamada görünen sahte gönderileri seed ile ürettiğimiz için zaman bilgisinin ancak saniye kısmında fark görebiliriz. Deneme yaparken iki durum arasında geçiş yapmadan önce Rails konsoldan exit komutu ile çıkıp tekrar girmeyi unutmayın.
posts_controller.rb dosyasında bir get_posts metodu tanımlamış ve içine en üstten 30 kaydı getiren tek satır bir kod yazmıştık. Ama daha sonra buraya döneceğimizi söylemiştik. Haydi dönelim:
app/controllers/posts_controller.rb
...
def get_posts
branch = params[:action]
search = params[:search]
category = params[:category]
if category.blank? && search.blank?
posts = Post.by_branch(branch).all
elsif category.blank? && search.present?
posts = Post.by_branch(branch).search(search)
elsif category.present? && search.blank?
posts = Post.by_category(branch, category)
elsif category.present? && search.present?
posts = Post.by_category(branch, category).search(search)
else
end
end
...
Daha önce lojiğin görseller içinde olmasının tercih edilmediğinden bahsetmiştik. Bu arama kodlarının da kontrolör içinde olması pek yakışık almadı. Önümüzdeki bölümlerde bu kodları düzenleyeceğiz.
Yukarıdaki kodda gördüğümüz gibi kullanıcının yaptığı olası girdilere göre değişik kapsamlar kullanılarak
Post modeli sorgulanıyor. Şimdi bunları model koduna ekleyelim.
app/models/post.rb
class Post < ApplicationRecord belongs_to :user belongs_to :category default_scope -> { includes(:user).order(created_at: :desc) } scope :by_category, -> (branch, category_name) do joins(:category).where(categories: {name: category_name, branch: branch}) end scope :by_branch, -> (branch) do joins(:category).where(categories: {branch: branch}) end scope :search, -> (search) do where("title LIKE lower(?) OR content LIKE lower(?)", "%#{search}%", "%#{search}%") end end
Not PostgreSQL kullanırken search sorgusunda ya LIKE yerine ILIKE kullanmak ya da lower(title) şeklinde aranan sütunu da küçük harfe çevirmek gerekebilir.
Yukarıdaki kodda joins metodu bağlantılı tablolardan kayıtları toplamak için kullanılır. Ayrıca girilen stringe göre kayıtlarda basit bir arama SQL satırı da var.
Eğer şimdi server’ı yeniden başlatırsak arama kutusunun çalıştığını görürüz. Ayrıca kolleksiyonların kategorilerine ait seçme butonları da çalışmaya başladı.
Sonsuz kaydırma
Kolleksiyon sayfalarında en alta indiğimizde kayıtların pagination ile sayfalara bölünmüş olduğunu görüyoruz. Sonraki linke tıkladığımızda daha eski kayıtların bulunduğu sayfalara doğru gidiyoruz. Burada Tweeter veya Facebook’taki gibi aşağıya gidildikçe eski gönderilerin sayfa yenilemeden aşağıda devam etmesini sağlayabiliriz. Güzel tarafı bunu yapmak çok kolay. Tüm yapmamız gereken biraz JavaScrşpt yazmak. Kullanıcı sayfanın en altına indiğinde bir AJAX çağrısı ile bir sonraki sayfadaki kayıtları alarak bulunulan sayfanın en altına eklenmesini sağlayabiliriz.
javascripts/posts klasöründe infinite_scroll.js dosyasını ekleyelim ve şu kodu içine yazalım:
app/assets/javascripts/posts/infinite_scroll.js
$(document).on("turbolinks:load", function() { var isLoading = false; if ($(".infinite-scroll", this).size()) > 0 { $(window).on("scroll", function() { var more_posts_url = $(".pagination a.next_page").attr("href"); var threshold_passed = $(window).scrollTop() > $(document).height() - $(window).height() - 60; if (!isLoading && more_posts_url && threshold_passed) { isLoading = true; $.getScript(more_posts_url).done(function(data, textStatus, jqxhr) { isLoading = false; }).fail(function() { isLoading = false; }); } }); } });
isLoading değişkeni aynı anda sadece bir yüklemenin olabilmesini garanti altına alır. Önce pagination var mı diye bakılıyor ve arkada başka sayfa var mı diye. Sonra bir sonraki sayfanın adresi alınıyor. Arkasından sayfa altına 60 piksel kalana kadar kaydırma yapıldıysa AJAX tetikleniyor. Son olarak getScript() fonksiyonu ile tüm koşullar gerçekleşirse sonraki sayfanın kayıtları alınıyor.
getScript() fonksiyonu da aynı JavaScript dosyasını tekrar yükleyeceği için PostsController içinde hangi dosyanın yayınlanacağını seçmemiz gerekiyor. posts_for_branch içinde hangi dosyanın yayınlanacağına karar verelim.
app/controllers/posts_controller.rb
...
def posts_for_branch(branch)
@categories = Category.where(branch: branch)
@posts = get_posts.paginate(page: params[:page])
respond_to do |format|
format.html
format.js { render partial: "posts/posts_pagination_page" }
end
end
...
Bu koda göre bir
.js dosya aksiyona ulaşırsa posts_pagination_page kalıp dosyası yayınlanacak .html erişim olursa değişen bir şey yok. Bu parça dosya yeni elemanları mevcut listeye ekleyecek. Bu yüzden o da bir JavaScript. Ama görsel kalıpların yanında bulunacak.
app/views/posts/_posts_pagination_page.js.erb
$("#feed").append("<%= j render @posts %>"); <%= render update_pagination_partial_path %>
Yeni yardımcı metodumuz
update_pagination_partial_path metodunu da posts_helper.rb içinde tanımlayalım.
app/helpers/posts_helper.rb
...
def update_pagination_partial_path
if @posts.next_page
"posts/posts_pagination_page/update_pagination"
else
"posts/posts_pagination_page/remove_pagination"
end
end
Burada
will_paginate gem’in next_page metodu başka sayfa olup olmadığını algılamak için kullanıldı.
app/views/posts/posts_pagination_page/_update_pagination.js.erb
$(".pagination").replaceWith("<%= j will_paginate @posts %>");
app/views/posts/posts_pagination_page/_remove_pagination.js.erb
$(window).off("scroll"); $(".pagination").remove();
Artık herhangi bir kolleksiyon sayfasında aşağı doğru kayarsak sonraki sayfaya ait kayıtların otomatik olarak geldiğini görürüz. Bu pagination menüyü görmeye artık ihtiyaç kalmadı. Onu da görünmez yapalım.
branch_page.scss stil dosyasına şunu ekleyelim:
app/assets/stylesheets/partials/posts/branch_page
.infinite-scroll { display: none; }
Bu bölüm de bu kadar. Mola verelim. Sonraki bölümde tekrar ana sayfaya dönüp bazı değişiklikler yapacağız. Görüşmek umuduyla , kalın sağlıcakla..
Sırada şunlar var:
- ORTA SEVİYE BİR RAİLS TUTORIAL - 6 (Ana sayfa güncellemesi)
- ORTA SEVİYE BİR RAİLS TUTORIAL - 7 (Anlık mesajlaşma ilavesi)




Hiç yorum yok:
Yorum Gönder