Привет, Хабр!
Работа с многоуровневыми формами в Ruby on Rails — это то, что точно поднимет ваш скилл в Ruby. В этой рассмотрим, как упростить этот процесс с помощью двух гемов: Wicked и Cocoon.
Многоуровневые формы позволяют собирать информацию от пользователя поэтапно, что делает интерфейс более дружелюбным.
Настройка и конфигурация гемов Wicked и Cocoon для Rails
Wicked
Для начала добавляем Wicked в Gemfile:
gem 'wicked'
Затем выполняем команду bundle install для установки гема.
Wicked превращает контроллер в магический многоэтапный процесс. Начинаем с создания нового контроллера или модификации существующего, добавив в него миксин Wicked::Wizard. Например:
rails g controller after_signup
В контроллер добавляем:
include Wicked::Wizard steps :first_step, :second_step
В config/routes.rb указываем, что контроллер должен использовать магические маршруты:
resources :after_signup, only: [:show, :update], controller: 'after_signup'
Юзаем render_wizard для управления переходами между шагами. Например:
def show @user = current_user render_wizard end
В update методе можно обработать ввод данных и переход к след. шагу.
Cocoon
Аналогично как и с Wicked, добавляем следующую строку в Gemfile:
gem 'cocoon'
Выполняемbundle install.
С Cocoon можно легко работать с вложенными атрибутами, например в форме можно использовать link_to_add_association и link_to_remove_association для динамического добавления или удаления полей.
Пример использования в форме с помощью simple_form:
<%= simple_form_for @user do |f| %> <%= f.simple_fields_for :addresses do |address| %> <%= render 'address_fields', f: address %> <% end %> <%= link_to_add_association 'add address', f, :addresses %> <% end %>
В частичном представлении _address_fields.html.erb можно использовать link_to_remove_association для добавления возможности удаления.
Cocoon автоматически работает с jQuery через application.js:
//= require jquery //= require cocoon
На стороне клиента можно использовать события Cocoon, такие как cocoon:before-insert и cocoon:after-insert, для добавления пользовательских анимаций или логики обработки:
$(document).on('cocoon:before-insert', function(e, insertedItem) { // анимация или другие действия перед вставкой элемента insertedItem.fadeIn('slow'); }); $(document).on('cocoon:after-insert', function(e, insertedItem) { // действия после вставки элемента });
Обработка и валидация данных формы
Rails предост��вляет множество хелперов для валидации, таких как presence, length, numericality, и format, которые помогают убедиться, что данные соответствуют определённым требованиям. Например, чтобы убедиться, что поле не пустое и соответствует определённой длине, можно использовать следующие валидации в модели:
class Comment < ApplicationRecord validates :content, presence: true, length: { maximum: 500 } end
Если данные не проходят валидацию, объект не будет сохранён в БД, а ошибки можно будет проверить через метод errors объекта.
Еще можно определить собственные методы валидации для более спец. требований, используя хелпер validate. Например, если нужно проверить, что текст не содержит ненормативной лексики, можно определить такой метод:
class Comment < ApplicationRecord validate :check_for_offensive_language private def check_for_offensive_language if content.include?('offensive_word') errors.add(:content, 'contains offensive language') end end end
При работе с формами и API, важно правильно обрабатывать ошибки валидации и передавать их юзеру. В контроллере можно организовать проверку валидности объекта и соответствующим образом формировать ответ:
class UsersController < ApplicationController def create user = User.new(user_params) if user.save render json: user, status: :created else render json: { errors: user.errors.full_messages }, status: :unprocessable_entity end end private def user_params params.require(:user).permit(:name, :email, :password) end end
Так можно отображать пользователю конкретные ошибки, связанные с каждым полем формы.
Тестирование
Для тестирования многоуровневых форм рекомендуется использовать сочетание RSpec и Capybara:
describe "Multi-step form", type: :feature do it "processes the form correctly" do visit new_registration_path fill_in 'Name', with: 'Kolya' click_button 'Next' fill_in 'Address', with: '123 Lenina St' click_button 'Submit' expect(page).to have_content('Registration successful') end end
Для детальной отладки можете использовать гем Pry. С ним можно остановить выполнение кода в любой точке и изучить текущее состояние переменных и логику выполнения:
class RegistrationsController < ApplicationController def create binding.pry # остановка и отладка в этой точке @user = User.new(user_params) if @user.save redirect_to @user else render :new end end end
Также не забываем логировать основные парамтеры:
Rails.logger.debug "Processing step 1 with data: #{params[:user]}"
Для более сложных многоуровневых форм можно юзать интеграционные тесты. Создавайте тестовые объекты с помощью FactoryBot и проверяйте ассоциации и валидации с помощью Shoulda Matchers:
describe User, type: :model do it { should validate_presence_of(:email) } it "has a valid factory" do user = build(:user) expect(user).to be_valid end end
Все лучшие практики веб-разработки на Ruby on Rails можно освоить на практическом онлайн-курсе.
