Hotwire: Broadcastable concern の修作

外観

目的
Action Cableの習作: stream_fromとbroadcast_to を Broadcastable concern で書いてみました。今回はモデルの永続化が必要になります。

環境
ruby 3.0.2p107
rails 7.0.0.alpha2

macOS 11.6
foreman 0.87.2 (gem install foreman)
Redis server 6.2.5 (brew install redis)

参照
Turbo Handbook / Integration with Server-Side Frameworks
broadcastable.rb

リポジトリ
https://github.com/usutani/try_hw_simple_broadcast

画面

画像1

雛形作成

rails new try_hw_simple_broadcast
cd try_hw_simple_broadcast
bundle remove jbuilder
bin/rails g model Message body
bin/rails db:migrate
bin/rails g controller Messages index

config/routes.rb
resources :messages, only: %i[ index create]
root to: "messages#index"

db/seeds.rb
Message.create([{ body: "Hello" }, { body: "こんにちは" }])
bin/rails db:seed

メッセージ一覧

app/controllers/messages_controller.rb
  def index
    @messages = Message.all
    @message = Message.new
  end

app/views/messages/index.html.erb
<div id="messages">
  <%= render @messages %>
</div>

touch app/views/messages/_message.html.erb
<div>
  <%= message.body %>
</div>

メッセージ作成

app/controllers/messages_controller.rb
  def create
    @message = Message.new(message_params)
    if @message.save
      redirect_to messages_url
    else
      @messages = Message.all
      render :index, status: :unprocessable_entity
    end
  end

  private

  def message_params
    params.require(:message).permit(:body)
  end

app/views/messages/index.html.erb
<div id="messages">
  <%= render @messages %>
</div>

<%= form_with model: @message do |form| %>
  <%= form.text_field :body %>
  <%= form.submit %>
<% end %>

以上で準備完了。

Procfile でサーバーを起動する。
foreman で Rails / Redis サーバーを起動する
bin/dev で開始。

作成したメッセージを turbo_stream で追加

  def create
    @message = Message.new(message_params)
    if @message.save
      respond_to do |format|
        format.turbo_stream
        format.html { redirect_to messages_url }
      end
    else
      @messages = Message.all
      render :index, status: :unprocessable_entity
    end
  end

touch app/views/messages/create.turbo_stream.erb
<%= turbo_stream.append "messages", @message %>

touch app/views/messages/index.turbo_stream.erb​

Messageモデルでbroadcastする

app/views/messages/index.html.erb
<%= turbo_stream_from :message %>

app/models/message.rb
class Message < ApplicationRecord
  after_create_commit :broadcast_later

  private

  def broadcast_later
    broadcast_append_later_to :message
  end
end

rm app/views/messages/create.turbo_stream.erb
rm app/views/messages/index.turbo_stream.erb

app/controllers/messages_controller.rb
  def create
    @message = Message.create(message_params)
  end

エラー表示

app/models/message.rb
class Message < ApplicationRecord
  after_create_commit :broadcast_later

  validates :body, length: { minimum: 2 }

  private

  def broadcast_later
    broadcast_append_later_to :message
  end
end

touch app/views/messages/create.turbo_stream.erb
<% if @message.errors.any? %>
  <%= turbo_stream.update :error_explanation do %>
    <h2><%= pluralize(@message.errors.count, 'error') %> prohibited this message from being saved:</h2>
    <ul>
      <% @message.errors.each do |error| %>
        <li><%= error.full_message %></li>
      <% end %>
    </ul>
  <% end %>
<% else %>
  <%= turbo_stream.update :error_explanation, '' %>
<% end %>

app/views/messages/index.html.erb
<%= turbo_stream_from :message %>

<div id="messages">
  <%= render @messages %>
</div>

<div id="error_explanation"></div>

<% form_data = { controller: "reset-form", action: "turbo:submit-end->reset-form#reset" }%>
<%= form_with model: @message, data: form_data do |form| %>
  <%= form.text_field :body %>
  <%= form.submit %>
<% end %>

補足

先にコントローラーに書いて疎通確認しても良いかも。

  def create
    @message = Message.new(message_params)
    if @message.save
      @message.broadcast_append_later_to "message"
    end
  end

stimulus reset_form フォームのリセット
但しこの方法ではエラー時に入力値がリセットされる。

turbo_stream create のエラー表示を部分テンプレート化
エラー表示を一般化する試み。

以上です。



この記事が気に入ったらサポートをしてみませんか?