# Terminal
bundle add hotwire-rails
rails hotwire:install
rails g scaffold tickets title
rails g model comment ticket:references
rails action_text:install
# app/javascript/packs/application.js
import Rails from "@rails/ujs"
// import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
Rails.start()
// Turbolinks.start()
ActiveStorage.start()
require("trix")
require("@rails/actiontext")
# layouts/application.html.erb
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= yield :head %>
<%= turbo_include_tags %>
# if using StimulusJS in Webpacker
<%# stimulus_include_tags %>
# models/ticket.rb
class Ticket < ApplicationRecord
has_rich_text :content
has_many :comments, dependent: :destroy
broadcasts
# after_create_commit -> { broadcast_append_to self }
# after_destroy_commit -> { broadcast_remove_to self }
# after_update_commit -> { broadcast_replace_to self }
end
# models/comment.rb
class Comment < ApplicationRecord
belongs_to :ticket
has_rich_text :content
broadcasts_to :ticket
# after_create_commit -> { broadcast_append_to ticket }
# after_destroy_commit -> { broadcast_remove_to ticket }
# after_update_commit -> { broadcast_replace_to ticket }
end
# tickets_controller.rb
def ticket_params
params.require(:ticket).permit(:title, :content)
end
# views/tickets/_form.html.erb
<div class="field">
<%= form.label :content %>
<%= form.rich_text_area :content %>
</div>
# views/tickets/show.html.erb
<%= turbo_stream_from @ticket %>
<%= turbo_frame_tag 'ticket' do %>
<%= render @ticket %>
<%= link_to 'Edit', edit_ticket_path(@ticket) %> |
<%= link_to 'Back', tickets_path, 'data-turbo-frame': :_top %>
<% end %>
<div id='comments'>
<h2>Comments</h2>
<%= render @ticket.comments %>
</div>
<%# link_to 'New Comment', new_ticket_comment_path(@ticket) %>
<%= turbo_frame_tag 'new_comment', src: new_ticket_comment_path(@ticket), target: :_top %>
# views/comments/_comment.html.erb
<%= content_tag :div, id: dom_id(comment) do %>
<em>Someone said <%= time_ago_in_words(comment.created_at) %> ago</em>
<%= comment.content %>
<hr>
<% end %>
# views/comments/new.html.erb
<%= turbo_frame_tag 'new_comment' do %>
<h2>New Comment</h2>
<%= form_with model: [@ticket, @comment],
data: { controller: 'reset_form', action: 'turbo:submit-end->reset_form#reset' } do |form| %>
<div class='field'>
<%= form.rich_text_area :content %>
<%= form.submit 'Post', 'data-reset_form-target': 'button' %>
</div>
<% end %>
<% end %>
# comments_controller.rb
class CommentsController < ApplicationController
before_action :set_ticket
def new
@comment = @ticket.comments.new
end
def create
@comment = @ticket.comments.create!(comment_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @ticket }
end
end
private
def set_ticket
@ticket = Ticket.find(params[:ticket_id])
end
def comment_params
params.require(:comment).permit(:content)
end
end
# config/routes.rb
Rails.application.routes.draw do
resources :tickets do
resources :comments, only: [:new, :create]
end
root to: 'tickets#index'
end
# views/tickets/_ticket.html.erb
<%= content_tag :div, id: dom_id(ticket) do %>
<h1><%= ticket.title %></h1>
<p><%= ticket.content %></p>
<% end %>
# views/comments/create.turbo_stream.erb
<%# turbo_stream.append 'comments', @comment %>
# assets/javascripts/controllers/reset_form_controller.js
import { Controller} from "stimulus"
export default class extends Controller {
static targets = ["button"]
reset() {
this.element.reset()
this.buttonTarget.disabled = false
}
}