Giới thiệu
Chào các bạn, khi chúng ta việc với chúng ta sẽ gặp các spec yêu cầu như là tạo form đăng ký step by step. Việc phải đáp ứng deadline cũng như yêu cầu về sự clear của code thì việc dùng Gem cũng là một giải pháp mà chúng ta muốn hướng đến. Trong bài viết ngày hôm nay thì mình muốn giới thiệu về gem wicked.
Mô tả bài toán
Khi tìm hiểu 1 cái gì đó, tốt nhất chúng ta nên đặt ra 1 output cho cái đó. Và kết quả bài chia sẻ này mình muốn đó là:
- Xây dựng một form-step có 3 step lưu được dữ liệu vào db.
- Mỗi step đều đảm bảo có validate dữ liệu.
- Phải đảm bảo cấu trúc code được tổ chức clear, dễ sử dụng
Cài đặt
khởi tạo dự án:
1 2 | rails new myapp |
Thêm dòng sau vào trong file Gemfile và chạy bundle install
1 2 | gem "wicked" |
Khởi tạo db và chạy rails db:migrate
1 2 | rails g model Users email:string password:string first_name:string last_name:string phone:string address:string |
Ý tưởng của gem wicked
Khi vào form đăng ký thì bước đầu tiên là sẽ tạo mới 1 bản ghi trống (chỉ có id được tự sinh ra). Rồi ở mỗi step sau đó chỉ đơn giản là việc update cho chính form đó mà thôi. Tất cả các xử lý logic của step sẽ được viết trong duy nhất 1 controller.
Còn về việc validate dữ liệu mình sẽ dùng context để lưu được trạng thái của từng step để có thể validate được, cái này mình sẽ trình bày ở phần sau.
Trong controller chỉ sử dụng duy nhất 2 function show
và update
:
- function
show
có nhiệm vụ hiển thị ra form tương ứng function - function
update
có nhiệm vụ update bản ghi vào db
- Một số helper của wicked ở 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 |
Một số helper của 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 |
Tiến hành
Xây dựng controller
Để xây dựng được form hoành chỉnh + code được clear rõ ràng thì mình sẽ xây dựng 2 controller, 1 controller làm nhiệm vụ tạo mới bản ghi, 1 controller xử lý logic của form. Cụ thể là:
UsersController: tạo mới bản ghi sau đó điều hướng tới steps controller .
StepsController: config form-step.
Cấu trúc controller
Tiến hành xây dựng 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 |
Tiếp đến là StepController.rb đây là controller xử lý logic của các step:
Đầu tiền chúng ta cần 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 |
Cấu trúc thư mục view
Khai báo bao nhiêu step thì sẽ tại bấy nhiêu file view ở trong views/steps
Chúng ta vẫn xây dựng form view như bình thường, từng form view sẽ có các attri tương ứng với step đó, tuy nhiên sẽ có 1 số chú ý sau:
- Thêm option method: :put (theo RESTful) để trỏ về hàm update.
- Gán url: wizard_path (đây là 1 method của wicked để lấy ra path của step hiện tại).
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 theo từng step
Tạo validate trong form-step:
Ý tưởng là mình sẽ dùng context (bối cảnh) để thực hiện validate theo từng ứng từng step:
Các bạn chú ý ở bước save mình đã truyền tên step vào context để model có thể validate đúng theo từng step được
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 |
Kết quả bài viết
Cảm ơn các bạn theo dõi bài viết của mình.