Rails will_paginate 如何計算 redirect_to 到指定那一頁

紅寶鐵軌客
Join to follow...
Follow/Unfollow Writer: 紅寶鐵軌客
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
寫程式中、折磨中、享受中 ......
694   0  
·
2018/07/11
·
5 mins read


我想大部分的網頁都還是有做分頁的,雖然無分頁(Pageless)是目前的趨勢,但是分頁還是有分頁的UX設計好處,特別是文章類的網站。

只要是有做分頁的網站,都會碰到一個問題,就是要如何 redirect_to 跳到指定的那一頁,這常常會發生在編輯或是新增之後,例如:部落格網站顯示文章列表,一般一頁是 25~30 個文章標題,用分頁顯示,但是,當我們要跳到指定的那一個文章顯示頁時,我們就會遇到不知道第幾頁的問題。 這問題很多解法,都是要讓後台程式算好,再跳到在那一頁,以下也算是我自己的紀錄,希望對喜愛用 rails 開發的人有幫助:

先算好頁數再跳 

這個最方便,當你資料儲存成功後,就可以計算頁數了,就三行,我是用 will_paginate gem

# 計算頁數
# 先找出會在分頁中列出的所有紀錄,這要與你的顯示頁同步
total_rec = Blog.where(xyz: 0).order(created_at: :asc)
# 再來找我們要跳到的 @blog 記錄在那裡
offset = total_rec.pluck(:id).index(@blog.id)
# 我們就可以算頁數了
# model.per_page 很方便,馬上就可以告訴我們這個 model 內是設定一頁幾個
page_num = (offset / Blog.per_page) + 1

Blog.where(xyz: 0)... 只是示意,你要自己依你的應用更改。

redirect_to 時還可以加上 id 或是 name 的 anchor 點,這樣就不只會跳到那頁,還可以停在你要的點.

 

進來後,再算頁數,再轉跳

這方法很弔詭,等進來 action 後,再計算 page 數,然後再轉跳,很有趣:

一,link_to params 你要去的地方:

我們不知道要去的地方是在那一頁,但是我們一定知道要去的地方是什麼,以上面的部落格網站為例,我們一定會知道要去的文章 id ,所以最簡單的辦法就是將這個 id 放入 link_to 的 params 裡面,別忘了給 params 取個好名:

link_to( "帶我去文章頁", blogs_path(id: @user, go_to_blog_id: blog.id.to_s) )

二,在 controller 中找到它在那一頁:

當 link_to 指到了 blog 的 action 後,我們來用已經知道的 blog id 來找它在那一頁,以下就是參考程式碼,一樣用的是 will_paginate gem

blog_pre = Blog.where(xyz: 0).order(:created_at)
@blog = blog_pre.paginate(page: params[:page])
# 找到這個 blog id 在那裡。
# 一定要沒有指定頁面及有指定轉頁,才會執行這轉頁動作
if ( !params.has_key?(:page) && params.has_key?(:go_to_blog_id) )
  x_offset = blog_pre.pluck(:id).index(params[:go_to_blog_id].to_i)
  # will_paginate 有很多內建查詢功能,.per_page 可以知道一頁有幾個,還有很多其他,可以到它的 Github 看看。
  x_page = x_offset / @blog.per_page + 1
  x_anchor = "anchor-"+params[:go_to_blog_id].to_s
  redirect_to blogs_path(id: @blog, :page => x_page.to_s, anchor: x_anchor) and return
end

這個例子已經加上了以 id 為 anchor 點的應用,這樣就不止可以轉到那ㄧ頁,還可以停在那個指定的文章上!

你可能會好奇,第一個先算好再跳簡單又好用,誰會用到第二個方法,進來後,再算頁數,再轉跳,這樣不就會有兩次 HTTP 動作,又慢又沒有意義,沒錯,但是實務上,我還真遇到一定要用到第二種方法的場景,這就是程式人生吧。

 

如何確保 page[:page] 正確

話說,正常的情況下,page[:page] 的值都是 will_paginate 產生的,不會錯,但是我們現在就是在改它,所以,就有可能會出錯了,最常發生的情況是 redirect_to 帶來了一個空的 page[:page] params,這很討厭,我們可以用 will_paginate 內建的 exception 來確保它的正確性:

# 在 helper 內加一個
# 不對的頁數,就設定成 1
def clean_page(page_number)
  begin
    WillPaginate::PageNumber(page_number)
  rescue WillPaginate::InvalidPage
    1
  end
end

# 在 controller 中,就可以直接呼叫了 (Rails 5.0+)
params[:page] = helpers.clean_page(params[:page])

 

跳不到指定的 Anchor 點

基本上,這樣就完成了,很簡單吧,但是如果你也寫了最後的anchor 點,也發現程式很乖的跳到指定頁後,就是不會跳到你所指定的 anchor 點,這個問題有一定的難度了,基本上,這是一個 redirect_to 的定義問題:

301 redirect 不執行 anchor 動作嗎?

redirect 301 按規定本來就不會跳,按規定,在 301 時,瀏覽器就不會執行任何網址在 hash 後面的東東,我也是因為這不會跳事件才剛學到的,當瀏覽器遇到 301 要求時,會把你指定的在 # 後的 anchor 拿掉,跟後台資料後,再將 anchor 加回你的網址列, 你根本就看不出來有那裡不對,只會覺得就是不跳,遇到鬼了,還好,此路不通,還是有路,解決的方法有以下幾種:

  1. 就用 javascript 吧:我知道說這樣的解法一定馬上很多人笑,這麼爛的解法,但是我覺得這是最簡的的解法,而且還可以帶出一些效果,是很爛的解法,但是簡單一直是我最喜愛的。
  2. 改用post 303:知道我不喜歡用這個方法的原因了吧,這樣的改法要改變Http verb,也就是說,如果如前例,本來的 index get 要改成 post,還要將 redirect_to 的 code 設成 303,好怪啊,我不喜歡,這種寫法以後要維護一定很慘,說實話,會不會動我也不知道,理論上應該會動啦,但是我連試都不想試。

很多人遇到這 anchor 不轉跳問題時,都會賴到 turbolinks,但是很快會發現,turbolinks 關掉後也沒用,不過,turbolinks 真的有把問體更「討厭化」,turbolinks 在 redirect_to 時,如果發現是 get action,就不會清 turbolinks 的 cache,我也是花了好久才知道這,也就是說,你如果不是用 javascript,你一樣要改用 non-get 的動詞,網頁才會跳到指定的 anchor,這真不是我喜歡的解法。

希望對大家有幫助。


WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.


Article info

Categories:
Tags:
Date:
Published: 2018/07/11 - Updated: 2019/11/09
Total: 1489 words


Share this article:
About the Author

很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯




Join the discussion now!
Don't wait! Sign up to join the discussion.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.