Introduce
Hi everyone, when working with us, we will meet the required spec such as creating a step-by-step registration form. Having to meet the deadlines as well as the requirements of the clear code, using Gem is also a solution that we want to work towards. In today’s article, I want to introduce about the wicked gem.
Describe the problem
When learning something, it’s best to set an output for that. And the result of this sharing I want is:
- Build a form-step with 3 steps that can save data to the db.
- Each step ensures data validation.
- Make sure the code structure is organized clear and easy to use
Setting
initialize project:
1 2 | rails new myapp |
Add the following line in Gemfile file and run bundle install
1 2 | gem "wicked" |
Initialize the db and run rails db:migrate
1 2 | rails g model Users email:string password:string first_name:string last_name:string phone:string address:string |
The idea of the wicked gem
When entering the registration form, the first step is to create a new blank record (only the id is generated automatically). Then at each step thereafter simply update the form itself. All step logic will be written in only one controller.
As for the data validation I will use the context to save the status of each step to be able to validate, this I will present in the following. In the controller, only use 2 function show
and update
:
- The function
show
is responsible for displaying the corresponding form function - The
update
function is responsible for updating the records into the db
- Some helper’s wicked in controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | steps :first, :second # Sets the order of steps step # Gets current step next_step # Gets next step previous_step # Gets previous step skip_step # Tells render_wizard to skip to the next logical step jump_to(:specific_step) # Jump to :specific_step render_wizard # Renders the current step render_wizard(@user) # Shows next_step if @user.save, otherwise renders render_wizard(@user, context: :account_setup) # Shows next_step if @user.save(context: :account_setup), otherwise renders wizard_steps # Gets ordered list of steps past_step?(step) # does step come before the current request's step in wizard_steps future_step?(step) # does step come after the current request's step in wizard_steps previous_step?(step) # is step immediately before the current request's step next_step?(step) # is step immediately after the current request's step |
Some helper of the view:
1 2 3 4 5 | wizard_path # Grabs the current path in the wizard wizard_path(:specific_step) # Url of the :specific_step next_wizard_path # Url of the next step previous_wizard_path # Url of the previous step |
Proceed
Building controller
To build a complete form + clear code, I will build 2 controllers, 1 controller to create new records, 1 controller handling the form’s logic. Specifically: UsersController: create a new record and then navigate to the steps controller. StepsController: config form-step.
Controller structure
Proceed to build UserController.rb
1 2 3 4 5 6 7 8 | class UsersController < Applicationcontroller def new @user = User.create redirect to user step path @user, :stepl end end |
Next is the StepController.rb which is the controller that handles the logic of the steps: First we need to include
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class Stepscontroller < Applicationcontroller before action :load_user # Thêm thư viện wicked include Wicked::Wizard # Khai báo các step steps :stepl, :step2, :Step3 def show # render ra step hiện tại renderwizard end def update @user.step = step # Lưu trạng thái step vào controller @user.update user_params context: step render wizard @user # render step tiêp theo end private def user params params.require(:user).permit(:email, password, :first name, :last name, :address,:phone) end def load user @user = User.find params[:user id] end end |
Directory view structure
Declare how many steps will result in multiple file views in views / steps We still build the form view as usual, each form view will have attri corresponding to that step, but there will be some notes:
- Add option method:: put (according to RESTful) to point to the update function.
- Assign url: wizard_path (this is a wicked method to get the path of the current step).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <%= form_with(model: @user, url: wizardpath, method: :put, local: true) do |form| %> <div class="field"> <%= form.label :email %> <%= form.text_field :email, id: :user_email %> </div> <div class="field"> <%= form.label :password %> <%= form.text_field :password, id: :user_password %> </div> <div class="actions"> <%= form.submit "Next step", "data-remote": false %> </div> <% end %> |
Validate step by step
Create validate in form-step: The idea is that I will use the context (context) to perform validate step by step: You notice in the save step, I have passed the step name into the context so that the model can be validated correctly for each step. step okay
1 2 3 4 5 | class User < ApplicationRecord validates :email, :passsword, presence: true, on: :stepl validates :first name, :last_name, presence: true, on: :step2 end |
Post results
Thank you for following up on my article.