Practical examples to master web forms in Rails
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)