Кнопка «Load More» с использованием Ajax и jQuery и пагинация страниц в Ruby on Rails

Сам я только новичок в изучении RoR, но при разработке собственного проекта возникло желание вместо классической пагинации с помощью гема will_paginate сделать ее более удобной при помощи javascript. Это должна быть классическая кнопка, при нажатии на которую загружаются следующие n-записей. Решение проблемы на русском языке я не нашел, на английском есть, но я нашел только громоздкие и неудобные варианты. Как говорится, хочешь сделать что-то хорошо — сделай это сам. Вот что получилось.

Разбиение на страницы будем производить с помощью все того же гема will_paginate, также мы будем его использовать если javascript у пользователя отключен.

Итак сначала устанавливаем гем:

gem 'will_paginate'


bundle install


Затем начинаем с контроллера. В моем случае это будет разбиение на страницы списка пользователей. Соответственно это действие 'index', которое должно отвечать на javascript (:js) запрос:

respond_to :html, :xml, :json, :js

 def index
    @users = User.paginate(page: session[:page])
    respond_with @users
 end


Хэш сесссии
session[:page]
используется в качестве счетчика. Чуть позже мы к нему вернемся.

Далее идем во вьюху index.html.erb и добавляем пагинацию:

<h1>All users</h1>

<ul class="users">
    <%= render @users %>
</ul>

<div id="without_button">
<%= will_paginate @users %>
</div>


<% if @users.next_page %>
<div id="load_more_users">
    <%= button_to 'More users', {action: "index", new_req: true }, method: :get, remote: true, class: "btn btn-primary btn-large" %> 
</div>
<% end %>


Поясню несколько моментов. Здесь у нас два блока. Первый c id «without_button» добавляет стандартную строку гема по разбиению страниц. Ее мы будем использовать когда javascript отключен:

image

Второй блок включает кнопку. Она будет показана только если количество юзеров достаточно для разбения на записи. По умолчанию для will_paginate это 30 записей. Соответственно если их 30 и меньше, метод next_page возвратит nil и блок загружен не будет. Если javascript отключен, кнопки быть не должно. Соответственно прописываем в css:

#load_more_users {
  display: none;
}


Кнопка передает управление нашему действию c хэшем params[:new_req], значение которого мы уставливаем в true. Объяснение будет дальше.

Итак теперь мы должны понять, что нам нужно загрузить следующие записи. Причем счетчик должен быть равным 1 (первая страница) при загрузке или обновлении страницы. Здесь нам помогает params[:new_req], пока не нажата кнопка он равен nil, если же равен true, то увеличиваем значение счетчика на 1. Еще один момент, если javascript отключен, то пагинация будет происходить через хэш params[:page]. А значит его значение мы и будем присваивать cчетчику. Надеюсь доступно объяснил. Итак мы создаем следующим метод:

private

 def up_page 
   session[:page] = case 
                   when params[:new_req] then session[:page]+1
                   when params[:page] then params[:page]
                   else 1
                   end
  end


Первоначально я использовал переменную класса в качестве счетчика, поскольку она не создается заново как переменная экземпляра при новом обращении к действию. Но товарищ ниже правильно заметил, переменная класcа будет общей для всех пользователей. Поэтому я заменил ее на хэш сессии.

Метод должен выполняться перед действием index:

 before_action :up_page, only: :index


Теперь самое интересное. Переходим к СoffeeScript. Добавлем следующий код в файл app/assets/javascripts/users.js.coffee

 $('#with_button').hide()
      $('#load_more_users').show()
      $('#load_more_users form').after('<button class="btn btn-primary btn-large btn-clone disabled" style="display: none;">More users</button>')
      $('#load_more_users').click ->
      	$('#load_more_users').find('form').hide().siblings('button').show()



Первая строчка скрывает классическую пагинацию, вторая показывает кнопку. Затем после нашей кнопки мы добавляем вторую кнопку-клона с затемненным фоном, которая будет показана вместо первой кнопки при клике на нее. В принципе кнопка-клон здесь не обязательна. Вместо нее можно показывать строку загрузки или любую другую картинку/гифку.

Осталось подключить Ajax. Добавляем следующий код в index.js.erb:

$('.users').append('<%= j render @users %>').siblings('#load_more_users').find("button").hide().siblings('form').show();

<% unless @users.next_page %>
    $('#load_more_users').remove();
<% end %>


После загрузки следующих n-записей пользователей мы скрываем кнопку-клона и снова показываем нашу форму с первой кнопкой. Кнопку-клона мы создаем для того, чтобы предотвратить повторное нажатие на кнопку, пока не загрузятся записи. Ну и удаляем нашу кнопку, когда все записи из нашего массива ActiveRecord::Relation уже показаны.

Вот и все. Надеюсь эта информация будет кому-нибудь полезна.

UPD.: Переменную класа @@page заменил на session[:page]
  • –2
  • 7,3k
  • 4
Поделиться публикацией

Похожие публикации

Комментарии 4
    0
    Переменная класса используется потому, что переменная экземпляра создается заново каждый раз при новом обращении к действию.


    А переменная класса будет общая для всех пользователей.
      0
      Вот меня этот момент и смущал.( Может есть какие-то советы по тому как лучше реализовать счетчик?
        0
        Все исправил. Теперь будет работать как надо. Спасибо за комментарий
        0
        Сессия — это тоже не самый лучший выбор. Чем плохи data-атрибуты элементов? При нажатии на кнопку можно отправлять ajax на href указанный в data с параметром page взятым из второго data-атрибута. В таком случае и форма, генерящаяся через button_to не понадобится, и js для отправки запросов будет единственный для всех батонов с таким функционалом. А в контроллере уж отдавать js.haml, который сделает всё что нужно на странице.
        Замена батонов тоже странный выбор. Disabled и/или замена текста в батоне не подходит? Стантардный колбэк для ajax-запросов с бэкграундом и гифкой загрузки тоже не устраивает?
        И, так сказать, крик души: пожалуйста, не делайте так: append(...).siblings(...).find(...).hide().siblings(...).show(); Разбивайте на логические части. Паровозы — это зло.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое