0

What I am trying to accomplish is a simple "toggle" checkbox for On/Off for a view which contains records from a model.

I have attempted to look into a "hidden" value, but it doesn't appear to work as intended.

How to get blank checkboxes to pass as false to params

When I've added: <%= hidden_field_tag "category_ids[]", '' %>

I receive Couldn't find Category with 'category_name'= when unchecking.

Essentially, the table is a Model.all, but I want to be able to modify the key value on an is_active column to true or false, depending on if the box is checked, or not. Currently, I can accomplish this for "checking" the box. But, not unchecking (passes null). I am trying accomplish this is one swoop rather than making all my checkes, and another pass for my unchecked. And, also bypassing the show/edit process. The table size is rather small, so I am not concerned with latency.

I have attempted to search as much as I could, but am at a loss. With what I've found I can do one or the other, but not both unfortunately, and I would greatly appreciate any guidance.

My view:

<h4>Categories</h4>
<%= form_tag enable_categories_path, method: :put do |f| %>
<table id="myTable" class="table table-bordered table-striped">
  <tr>
    <th>Enable</th>
    <th>Category Name</th>
    <th>Active</th>
  </tr>

  <tbody>
  <% @categories.each do |category| %>
    <tr>
      <td>
        <%= check_box_tag "category_ids[]", category.id, category.is_active == 1 ? 'checked' : nil %>
      </td>
      <td><%= link_to category.category_name, category %></td>
      <td><%= category.is_active == 1 ? 'Yes' : 'No' %></td>
    </tr>
  <% end %>
  </tbody>
</table>
<%= render 'settings/button' %>
<% end %>

Here, the checkboxes are grabbing their state from the model itself for the corresponding record, so if no action is taken on the checkbox it remains the same (or passes state back)

My controller:

class CategoriesController < ApplicationController
  before_action :set_category, only: [:show, :edit, :update]

  def index
    @categories = Category.all.order(:category_sort)
  end

  def show
    @category = Category.find(params[:id])
  end

  def edit
  end

  def update
    if @category.update(category_params)
      redirect_to categories_path
    else
      render :edit
    end
  end

  def enable
    Category.update(params[:category_ids], params[:category_ids].map{ |e| {is_active: true} })
    redirect_to categories_path
  end

  private

  def set_category
    @category = Category.find(params[:id])
  end

  def category_params
    params[:category].permit(:id, :category_name, :is_active)
  end

end

Currently, I'm only passing is_active: true, until I can figure a way to pass ALL checkbox states.

My model:

class Category < ActiveRecord::Base
  self.table_name  = 'categories'

  has_many   :metrics
end

My route:

resources :categories do
    collection do
      put :toggle
    end
  end

Everything appears to pass correctly for checked boxes. But, nothing appears in the logs for when something is unchecked.

dinjas
  • 2,115
  • 18
  • 23
Nomad
  • 250
  • 3
  • 11
  • 27
  • 1
    Could you implement collection_check_boxes in someway? https://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes – Beartech Apr 04 '18 at 22:10
  • I believe collection_check_boxes work best with associations. Where as categories doesn't have associations (at least in the way the edit is being performed) – Nomad Apr 04 '18 at 22:20
  • Could you explicitly pass `true`/`false` as the value for the checkboxes? – Beartech Apr 04 '18 at 23:36
  • 1
    I guess I'm unclear, is the `is_active` column a boolean? – Beartech Apr 04 '18 at 23:40

2 Answers2

1

When I've run into situations like this in Rails I usually end up using AJAX rather than mass assignment. It's actually rather easy. At least easier for me than learning the inner workings of the check_boxes and collection_check_boxes, LOL

A simple Categories table:

    <table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Status</th>
    </tr>
  </thead>

  <tbody>
    <% @categories.each do |category| %>
      <tr>
        <td><%= category.category_name %></td>
        <% status = category.is_active? ? 'Active' : 'Inactive' %>
        <td id="<%= category.id %>" ><button class="button_<%= status %>"><%= link_to status, toggle_active_path(:id => category.id), :method => :post, :remote => true %></button></td>
      </tr>
    <% end %>
  </tbody>
</table>

The lines to note are the embedded ruby setting the status variable. This is used to set the value of the class for the button. In the CSS file the button class button_Active sets the color to green and button_Inactive makes it red. It also uses this variable to set the text of the button.

enter image description here

Create a method in the controller that toggles the status:

def toggle_is_active
@category.toggle!(:is_active) #flips the boolean on is_active
respond_to do |format|
  format.html { redirect_to(@category) }
  format.js
end

Be aware that .toggle! will bypass model validations and save. If you need to ensure validations run you can use @category.toggle(:is_active).save

This will respond to format.js and call a very small file called toggle_is_active.js.erb:

$('#<%= @category.id %>').html('<%= escape_javascript(render :partial => 'toggle_active') %>');

This targets the id of the html element that we set to the id of the category in the table row.

This calls a partial called _toggle_active.html.erb and updates the appropriate <td> with the new html:

<% status = @category.is_active? ? 'Active' : 'Inactive' %>
<button class="button_<%= status %>"><%= link_to status, toggle_active_path(:id => @category.id), :method => :post, :remote => true %></button>

Now all you need is a route to access the AJAX link:

routes.rb:

post 'toggle_active' => 'categories#toggle_is_active'

Here is a working example you can clone on github. It has the stylesheets to get the look above. I think you can extrapolate this for almost any situation:

https://github.com/Beartech/category_boxes

Beartech
  • 6,173
  • 1
  • 18
  • 41
  • I use a solution like this because I don't need a form and I can put them anywhere. If you are using something like Bootstrap it's easy to style the buttons using a `class: "btn ..."` at the end of the `link_to`. – Beartech Apr 05 '18 at 04:44
0

Let's consider what's going on here:

  def enable
    Category.update(params[:category_ids], params[:category_ids].map{ |e| {is_active: true} })
    redirect_to categories_path
  end

Can you post what params[:category_ids] looks like here? the map here doesn't quite make sense. Also, what is the data type of is_active in the database?

lacostenycoder
  • 10,623
  • 4
  • 31
  • 48