![](https://i.imgur.com/r279VHK.png) 最近剛好在實作捲軸下拉時,動態顯示出剩下來分頁的內容,所以參考了[RailsCast Endless Page](http://railscasts.com/episodes/114-endless-page-revised)的影片,以下是實作後的筆記。 ## 先做出簡單的頁面 假設我們需要在index page實作列出所有portfolio的效果 ![](https://i.imgur.com/NLScYTd.png) 我們最一開始的view和controller應該會長這樣 ```ruby= # home/index.html.erb <div id="products" class="row"> <% @portfolios.each do |portfolio| %> <div class="col-lg-2 col-sm-6 portfolio-item product"> <div class="card h-100"> <a href="#"> <%= image_tag portfolio.image.url, class: "card-img-top"%> </a> </div> </div> <% end %> </div> ``` ```ruby= # home_controller.rb def index @portfolios = Portfolio.all end ``` 為了等下的實作方便,讓我們來改寫一下目前的頁面,我們要把程式碼移到partial中,然後用render來渲染。 ```ruby= # home/index.html <div id="products" class="row"> <%= render @portfolios %> </div> ``` ```ruby= # portfolios/_potfolio.html.erb <div class="col-lg-2 col-sm-6 portfolio-item product"> <div class="card h-100"> <a href="#"> <%= image_tag portfolio.image.url, class: "card-img-top"%> </a> </div> </div> ``` > 註:render collection的用法,可以參考龍哥的這一篇[Layout, Render 與 View Helper](https://railsbook.tw/chapters/15-layout-render-and-view-helper.html)的內容。 ## 實作分頁功能 接下來我們要實作分頁功能,因為我們的無限捲軸功能也是透過javascript去產生原本頁面上的分頁來完成的。 首先我們需要使用`kaminari`這個gem,在Gemfile裡寫入 ```ruby= # Gemfile gem 'kaminari' ``` 執行bundle安裝gem ``` bundle install ``` 接下來我們在`home_controller`中,加上以下的程式碼 ```ruby= def index @portfolios = Portfolio.all.page(params[:page]).per(6) end ``` 讓我們看一下我們都新增了哪些東西 - page(params[:page]) 這段程式碼可以params判別目前的頁數是第幾頁。比如說當我們點擊第四頁的時候,我們就可以透過`params[:page]`得到目前的頁數。 ``` http://localhost:3000/?page=4 ``` - per(6) 這代表你每幾個objects一頁。 接下來在view中加入`<%= paginate @portfolios %>`,這段程式碼可以幫我們產生分頁所需的link。 ```ruby= <div id="products" class="row"> <%= render @portfolios %> </div> <%= paginate @portfolios %> ``` ![](https://i.imgur.com/TVzVche.png) 到這邊我們就已經產生了分頁的效果。 ## 處理滾動捲軸的內容 接下來我們要實作的內容是,當向下滾動到某個區域的時候,網頁會自動顯示接下來的分頁。 - 卷軸滾動到特定位置時觸發 - 即時渲染下一個分頁的內容 ### 卷軸滾動到特定位時觸發 首先先在`home.coffee`中加入以下的程式碼,我們會一步一步來解釋 ```javascript= jQuery -> $(window).scroll -> if $(window).scrollTop() > $(document).height() - $(window).height() - 60 alert "Near Bottom" ``` - $(window).scroll() 代表當我們滾動視窗時,就會觸發內部的程式碼 - $(window).scrollTop() `scrollTop()`可以讓我們設定該元素(這裡是window)跟頂部的位移。當你尚未開始滾動時,位移是零。 - $(window).height() 和 $(document).height() 這可以讓我們得到document跟目前視窗的高度 所以這段程式碼的邏輯就是,當頁面向下滾動時,如果頁面的偏移量,大於整個document的高度 - 視窗的高度 - 60時,觸發alert。 ### 即時渲染下一個分頁的內容 接下來讓我們改寫並加入新的程式碼到`home.coffee`和`home/index.js.erb`兩個檔案中。 #### home.coffee程式碼 ```ruby= # home.coffee jQuery -> $(window).scroll -> url = $('.pagination .next a').attr('href') if url && $(window).scrollTop() > $(document).height() - $(window).height() - 60 $('.pagination').text("載入更多圖片") $.getScript(url) ``` - url = $('.pagination .next a').attr('href') 這段程式碼會得到頁面中,Next的anchor的href值。這段程式碼的重要之處在於,我們接下來可以透過url還有query string,幫我們取得「下一頁」應該要渲染出的object,這樣我們就可以透過滾動到底部時取得下一頁的內容。 我們可以看一下以下的截圖。 ![](https://i.imgur.com/qsbrhLS.png) 而我們的url就會是`/?page=2`,這邊對應的path就是`root_path`,並加上一段query string(`page=2`)。 - $('.pagination').text("載入更多圖片") 設置classj為paginate的element中的text。 ![](https://i.imgur.com/jhBf5iT.png) - $.getScipt(url) getScript其實是以下程式碼的簡寫 ```javascript= $.ajax({ url: url, dataType: "script", success: success }); ``` 目的是用來送出ajax 的GET request。所以我們可以知道getScript會對我們剛剛得到的`/?page=2`送出一個ajax的GET request。 ### index.js.erb 接續剛剛上一段的`$.getScipt(url)`,其實就是對url送出get請求。 由於我們的`root_path`(`root 'home#index'`)對應到的是`home_controller`下的index method,所以我們如果可以在`app/view/home`的資料夾下,新增一個`index.js.erb`檔來處理request format為js的情況。 > 註:在*.js.erb這類的檔案中,我們可以混用ruby與js,然後編譯成js code後再傳回去給client端。 ```ruby= # home/index.js.erb $('#products').append('<%= j render(@portfolios) %>'); <% if @portfolios.next_page %> $('.pagination').replaceWith('<%= j paginate(@portfolios) %>'); <% else %> $('.pagination').remove(); <% end %> <% sleep 1 %> ``` - $('#products').append('<%= j render(@portfolios) %>'); 這段程式碼會在id為products的element後方,插入`render(@portfolios)`渲染出的html。 這邊另外比較值得注意的是`j()`的用法。 `j()`其實是`escape_javascript()`的縮寫,目的是跳脫雙引號帶來的束縛,不會因為在內部出現多個雙引號導致javascript壞掉。更多內容可以參考這篇[Rails為何要使用escape_javascript?](http://noelsaga.herokuapp.com/blog/2015/09/25/railswei-he-yao-shi-yong-escape-javascript/)。 如果你還記得的話,我們的`home_controller`中的index方法是 ```ruby= def index @portfolios = Portfolio.all.page(params[:page]).per(6) end ``` 當我們對home/index送出get request時,我們還額外送出了一個query string`?page=2`。所以我們等於是透過送出ajax request還有query string,得到了「下一頁(第二頁)」中的object。 - <% if @portfolios.next_page %> $('.pagination').replaceWith('<%= j paginate(@portfolios) %>'); <% else %> $('.pagination').remove(); <% end %> 這段程式碼代表的意思是,當目前的頁面的下一頁還有object時,將目前的分頁link用下一頁的分頁link取代掉。也就是假設目前是第一頁,當我們往下滾到定點時,用第二頁的分頁link取代第一頁。 ![](https://i.imgur.com/uEOUccL.png) ![](https://i.imgur.com/5oSbwdJ.png) 這樣的話我們就可以一直不斷地抓取下一頁的內容。 - <% sleep 1 %> 讓程式暫停1秒再繼續執行。這邊可以自行決定要不要使用,如果不希望分頁滾動時渲染太快,可以考慮使用。 ## 參考資料 [RailsCast #114 Endless Page](http://railscasts.com/episodes/114-endless-page) [Rails為何要使用escape_javascript?](http://noelsaga.herokuapp.com/blog/2015/09/25/railswei-he-yao-shi-yong-escape-javascript/) [Why escape_javascript before rendering a partial?](https://stackoverflow.com/questions/1620113/why-escape-javascript-before-rendering-a-partial) [Layout, Render 與 View Helper](https://railsbook.tw/chapters/15-layout-render-and-view-helper.html) 註:圖片取自[Pixabay 創用CC](https://pixabay.com/en/scroll-parchment-document-pergament-152864/)並自行修改
post_id | 50,655,804 |
---|---|
author | linjiahung |
permlink | rails |
category | cn |
json_metadata | "{"image": ["https://i.imgur.com/r279VHK.png"], "links": ["http://railscasts.com/episodes/114-endless-page-revised", "https://railsbook.tw/chapters/15-layout-render-and-view-helper.html", "http://noelsaga.herokuapp.com/blog/2015/09/25/railswei-he-yao-shi-yong-escape-javascript/", "http://railscasts.com/episodes/114-endless-page", "https://stackoverflow.com/questions/1620113/why-escape-javascript-before-rendering-a-partial", "https://pixabay.com/en/scroll-parchment-document-pergament-152864/"], "format": "markdown", "tags": ["cn", "cn-reader", "ruby", "rails"], "users": ["portfolios", "portfolios.next"], "app": "steemit/0.1"}" |
created | 2018-05-28 13:18:12 |
last_update | 2018-05-29 07:07:51 |
depth | 0 |
children | 1 |
net_rshares | 5,647,153,553 |
last_payout | 2018-06-04 13:18:12 |
cashout_time | 1969-12-31 23:59:59 |
total_payout_value | 0.019 SBD |
curator_payout_value | 0.002 SBD |
pending_payout_value | 0.000 SBD |
promoted | 0.000 SBD |
body_length | 5,552 |
author_reputation | 52,749,970,637 |
root_title | [Rails]實作捲軸產生無限捲軸功能 |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 SBD |
percent_steem_dollars | 10,000 |
author_curate_reward | "" |
voter | weight | wgt% | rshares | pct | time |
---|---|---|---|---|---|
swagger | 0 | 53,319,226 | 0.03% | ||
linjiahung | 0 | 610,390,398 | 100% | ||
cn-cutie.pie | 0 | 4,983,443,929 | 25.8% |
@linjiahung, 这是小可可我在steemit最好的邂逅,好喜欢你的贴(^∀^)哇~~~ ![img](https://i.imgur.com/cd4haG7.png)
post_id | 50,657,894 |
---|---|
author | cn-cutie.pie |
permlink | 20180528t133606697z-post |
category | cn |
json_metadata | "{"tags": ["cn"]}" |
created | 2018-05-28 13:36:06 |
last_update | 2018-05-28 13:36:06 |
depth | 1 |
children | 0 |
net_rshares | 4,574,885,974 |
last_payout | 2018-06-04 13:36:06 |
cashout_time | 1969-12-31 23:59:59 |
total_payout_value | 0.000 SBD |
curator_payout_value | 0.000 SBD |
pending_payout_value | 0.000 SBD |
promoted | 0.000 SBD |
body_length | 88 |
author_reputation | 717,060,097,040 |
root_title | [Rails]實作捲軸產生無限捲軸功能 |
beneficiaries | [] |
max_accepted_payout | 1,000,000.000 SBD |
percent_steem_dollars | 10,000 |
author_curate_reward | "" |
voter | weight | wgt% | rshares | pct | time |
---|---|---|---|---|---|
steemitstats | 0 | 3,632,364,610 | 5% | ||
cn-naughty.boy | 0 | 942,521,364 | 5% |