Ö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