Railsamples

Practical examples to master web forms in Rails

Other - HABTM Checkboxes

This example shows how to assign multiple tags to articles through checkboxes.

ENV["SECRET_KEY_BASE"] = "1212312313"
ENV["DATABASE_URL"] = "sqlite3:///#{__dir__}/database.sqlite"

require "bundler/inline"

gemfile do
  source "https://www.rubygems.org"
  gem "uni_rails", "0.4.1"
  gem "sqlite3", "~> 1.7"
  gem "byebug"
end

require "uni_rails"
require "sqlite3"
require "byebug"

#  ==== ROUTES ====

UniRails::App.routes.append do
  root "posts#index"
  resources :posts
end

#  ==== DB SCHEMA ====

ActiveRecord::Base.establish_connection
ActiveRecord::Schema.define do
  create_table :posts, force: :cascade do |t|
    t.string :title
    t.timestamps
  end

  create_table :tags, force: :cascade do |t|
    t.string :name
    t.timestamps
  end

  create_join_table :posts, :tags, force: true
end

#  ==== MODELS ====

class Post < ActiveRecord::Base
  has_and_belongs_to_many :tags

  validates :title, presence: true

  def tag_names
    tags.pluck(:name)
  end
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :post
end

#  ==== SEEDS ====

%w[Help Learning Question News Tutorial Discussion].each do |tag_name|
  Tag.create(name: tag_name)
end

Post.create(title: 'A new Ruby gem')
Post.create(title: 'Hello Rails World')
Post.create(title: 'That neat Rails form trick')

#  ==== CONTROLLERS ====

class PostsController < ActionController::Base
  layout 'application'

  def index
    @posts = Post.includes(:tags).all
  end

  def new
    @post = Post.new
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      redirect_to posts_path, notice: 'created successfully'
    else
      render :new
    end
  end

  def edit
    @post = Post.find(params[:id])
  end

  def update
    @post = Post.find(params[:id])
    if @post.update(post_params)
      redirect_to posts_path, notice: 'updated successfully'
    else
      render :edit
    end
  end

  def destroy
    Post.find(params[:id]).destroy
    redirect_to posts_path, notice: 'destroyed successfully'
  end

  private

  def post_params
    params.require(:post).permit(:title, tag_ids: [])
  end
end

#  ==== CSS ====

UniRails.css <<~CSS

  html { background-color:#EEE; }
  body { width:500px; height:700px; margin:auto;
    background-color:white; padding:1rem;
  }
  .errors { color: red; }
  .field_with_errors { color: red;  display: inline; }
  table { width:100%;
    table-layout: fixed;
    th, td { text-align:left; padding: .5rem; border: 1px solid black; }
    .actions { a, .button_to { display: inline; } }
  }
CSS


#  ==== VIEWS ====

UniRails.register_view "posts/index.html.erb", <<~HTML

  <p style="color: green"><%= notice %></p>

  <h1>Posts</h1>
  <p><%= link_to 'Create new post', new_post_path %></p>

  <table>
    <thead>
      <tr>
        <th>Title</th>
        <th>Tags</th>
        <th>Actions</th>
      </tr>
    </thead>
    <tbody>
      <% if @posts.present? %>
        <% @posts.each do |post| %>
          <tr>
            <td><%= post.title %></td>
            <td><%= post.tag_names.to_sentence %></td>
            <td class="actions">
              <%= link_to 'Edit', edit_post_path(post) %>
              <%= button_to 'Destroy', post, method: :delete %>
            </td>
          </tr>
        <% end %>
      <% else %>
        <tr><td colspan="3">There are no posts</td></tr>
      <% end %>
    </tbody>
  </table>
HTML


UniRails.register_view "posts/_form.html.erb", <<~HTML

  <%= form_with model: post do |f| %>
    <p class="errors"><%= post.errors.full_messages.to_sentence %></p>
    <%= f.label :title %>
    <%= f.text_field :title %>

    <fieldset>
      <legend>tags</legend>
       <%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %>
    </fieldset>

    <p>
      <%= f.submit %>
      <%= link_to 'Cancel', :back %>
    </p>
  <% end %>
HTML


UniRails.register_view "posts/new.html.erb", <<~HTML

  <h1>New Post</h1>

  <%= render 'form', post: @post %>
HTML


UniRails.register_view "posts/edit.html.erb", <<~HTML

  <h1>Edit <%= @post.title %></h1>

  <%= render 'form', post: @post %>
HTML


UniRails.run(Port: 3000)