Сам я только новичок в изучении RoR, но при разработке собственного проекта возникло желание вместо классической пагинации с помощью гема will_paginate сделать ее более удобной при помощи javascript. Это должна быть классическая кнопка, при нажатии на которую загружаются следующие n-записей. Решение проблемы на русском языке я не нашел, на английском есть, но я нашел только громоздкие и неудобные варианты. Как говорится, хочешь сделать что-то хорошо — сделай это сам. Вот что получилось.
Разбиение на страницы будем производить с помощью все того же гема will_paginate, также мы будем его использовать если javascript у пользователя отключен.
Итак сначала устанавливаем гем:
Затем начинаем с контроллера. В моем случае это будет разбиение на страницы списка пользователей. Соответственно это действие 'index', которое должно отвечать на javascript (:js) запрос:
Хэш сесссии
Далее идем во вьюху index.html.erb и добавляем пагинацию:
Поясню несколько моментов. Здесь у нас два блока. Первый c id «without_button» добавляет стандартную строку гема по разбиению страниц. Ее мы будем использовать когда javascript отключен:

Второй блок включает кнопку. Она будет показана только если количество юзеров достаточно для разбения на записи. По умолчанию для will_paginate это 30 записей. Соответственно если их 30 и меньше, метод next_page возвратит nil и блок загружен не будет. Если javascript отключен, кнопки быть не должно. Соответственно прописываем в css:
Кнопка передает управление нашему действию c хэшем params[:new_req], значение которого мы уставливаем в true. Объяснение будет дальше.
Итак теперь мы должны понять, что нам нужно загрузить следующие записи. Причем счетчик должен быть равным 1 (первая страница) при загрузке или обновлении страницы. Здесь нам помогает params[:new_req], пока не нажата кнопка он равен nil, если же равен true, то увеличиваем значение счетчика на 1. Еще один момент, если javascript отключен, то пагинация будет происходить через хэш params[:page]. А значит его значение мы и будем присваивать cчетчику. Надеюсь доступно объяснил. Итак мы создаем следующим метод:
Первоначально я использовал переменную класса в качестве счетчика, поскольку она не создается заново как переменная экземпляра при новом обращении к действию. Но товарищ ниже правильно заметил, переменная класcа будет общей для всех пользователей. Поэтому я заменил ее на хэш сессии.
Метод должен выполняться перед действием index:
Теперь самое интересное. Переходим к СoffeeScript. Добавлем следующий код в файл app/assets/javascripts/users.js.coffee
Первая строчка скрывает классическую пагинацию, вторая показывает кнопку. Затем после нашей кнопки мы добавляем вторую кнопку-клона с затемненным фоном, которая будет показана вместо первой кнопки при клике на нее. В принципе кнопка-клон здесь не обязательна. Вместо нее можно показывать строку загрузки или любую другую картинку/гифку.
Осталось подключить Ajax. Добавляем следующий код в index.js.erb:
После загрузки следующих n-записей пользователей мы скрываем кнопку-клона и снова показываем нашу форму с первой кнопкой. Кнопку-клона мы создаем для того, чтобы предотвратить повторное нажатие на кнопку, пока не загрузятся записи. Ну и удаляем нашу кнопку, когда все записи из нашего массива ActiveRecord::Relation уже показаны.
Вот и все. Надеюсь эта информация будет кому-нибудь полезна.
UPD.: Переменную класа
Разбиение на страницы будем производить с помощью все того же гема 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 отключен:

Второй блок включает кнопку. Она будет показана только если количество юзеров достаточно для разбения на записи. По умолчанию для 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]