12/08/2018, 14:02

Build on/off toggle state function in Rails

Xây dựng chức năng chuyển trạng thái on-off trong Rails app. Trong quá trình làm trang web với Rails, mình cần làm một chức năng là người dùng click thay đổi trạng thái on/off và lưu ngay vào database. Có rất nhiều cách làm cho chức năng này, ở đây mình sử dụng ajax để gửi data lên server và ...

Xây dựng chức năng chuyển trạng thái on-off trong Rails app.

Trong quá trình làm trang web với Rails, mình cần làm một chức năng là người dùng click thay đổi trạng thái on/off và lưu ngay vào database.

Có rất nhiều cách làm cho chức năng này, ở đây mình sử dụng ajax để gửi data lên server và cập nhật view sau khi update trên server thành công.

Bài toán

Bài toán đặt ra là mình có một bảng option_groups, bảng này có một trường lưu trữ trạng thái của các bản ghi gọi là is_active. Công việc đặt ra là khi người quản trị click vào button on/off thì sẽ chuyển trạng thái tương tự và cập nhật vào database.

Triển khai

Đầu tiên mình sẽ hiển thị danh sách các option_groups tại trang index theo dạng bảng.

Hiển thị danh sách

Trong controller app/controllers/option_groups_controller.rb

class OptionGroupsController < ApplicationController
  def index
    @option_groups = OptionGroup.all
  end

Tiếp theo là hiển thi ra view

app/views/option_groups/index.slim

.ibox-content#js-option-group-list
  table.table.table-striped.table-hover.table-bordered
    thead
      tr
        th.col-sm-1.text-center= t(".status")
        th= t(".option_group_name")
    tbody
      - @option_groups.each do |option_group|
        tr
          td.text-center
            = status_for option_group
          td= link_to option_group.name, option_group_path(option_group)

Như phía trên là mình hiển thị danh sách trong bảng. Có hàm status_for, mình sẽ định nghĩa hàm này trong helpers để mục đích hiển thị ra icon cùng một số thuộc tính cho on/off.

# helpers/option_groups_helper.rb

class OptionGroupsHelper
  def status_for option_group
    if option_group.is_active
      content_tag(:i, "", class: "fa fa-toggle-on fa-2x cursor-pointer on-off-swap",
        data: {id: option_group.id})
    else
      content_tag(:i, "", class: "fa fa-toggle-off fa-2x cursor-pointer on-off-swap",
        data: {id: option_group.id})
    end
  end
end

Cập nhật thay đổi trạng thái vào database

Tiếp theo mình sẽ tiến hành sử dụng ajax để gửi request lên server update lại trạng thái cho option_group tại trường is_active.

Bổ sung thêm vào controller cho hàm update

# app/controllers/option_groups_controller.rb

def update
  @option_group = OptionGroup.find params[:id]

  respond_to do |format|
    if @option_group.update(option_group_params)
      format.json {head :no_content}
    else
      format.json {render json: @option_group.errors, status: :unprocessable_entity}
    end
  end
end

private
def option_group_params
  params.require(:option_group).permit :is_active, :name
end

Bởi vì ở đây mình update sử dụng json, do đó mình không viết dạng trả về kiểu html.

Sử dụng ajax để gửi data lên server và update trạng thái.

$("#js-option-group-list tbody").on "click", ".on-off-swap", ->
  id = $(@).data "id"
  $status_field = $(@).parent().children "i"

  fa_toggle_class = {
    0: "fa-toggle-off",
    1: "fa-toggle-on"
  }

  if $status_field.hasClass "fa-toggle-off"
    new_status = 1
  else
    new_status = 0

  $.ajax
    type: "PATCH"
    url: "/option_groups/#{id}"
    data: option_group: {is_active: new_status}
    dataType: "json"
    success: () ->
      $status_field.removeClass("fa-toggle-off fa-toggle-on")
        .addClass fa_toggle_class[new_status]
      return

Như vậy mỗi khi cập nhật bằng ajax thành công thì trên view sẽ cập nhật thay đổi theo.

Thêm rspec cho hàm helper vừa tạo

describe "status_for" do
  subject{helper.status_for option_group}

  context "when is_active is false" do
    let(:option_group){create :option_group, is_active: false}

    it{is_expected.to eq content_tag(:i, "",
      class: "fa fa-toggle-off fa-2x cursor-pointer on-off-swap",
      data: {id: option_group.id})}
  end

  context "when is_active is true" do
    let(:option_group){create :option_group}

    it{is_expected.to eq content_tag(:i, "",
      class: "fa fa-toggle-on fa-2x cursor-pointer on-off-swap",
      data: {id: option_group.id})}
  end
end
0