Techioz Blog

ターボ ストリームとカスタム アクション ケーブル チャネルの接続の問題

概要

次のような erb ビューからカスタム アクション ケーブル チャネルを使用してターボ ストリーム接続をセットアップしようとしています。

ビュー

<%= turbo_stream_from "image_viewer_#{@image.id}", channel: "ImageViewerChannel", data: {image_id: @image.id} %>
<%= turbo_frame_tag "connected_users" do %>
  <%= render 'connected_users', count: 0 %>
<% end %>

チャネル

class ImageViewerChannel <  ApplicationCable::Channel
  extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
  include Turbo::Streams::StreamName::ClassMethods

  @@connected_users_count = 0

  def subscribed
    @@connected_users_count += 1
    @image_id = params[:image_id]
    stream_from "image_viewer_#{@image_id}"
    broadcast_connected_users_count
  end

.....


  private

  def broadcast_connected_users_count
    ActionCable.server.broadcast(
      "image_viewer_#{@image_id}",
      Turbo::StreamsChannel.broadcast_replace_to(
        "connected_users",
        target: "connected_users",
        partial: "images/connected_users",
        locals: { count: @@connected_users_count }
      )
    )
  end

しかし、接続が適切に機能しているようには見えません。redisが実行されていて、チャネルがエレバントストリーム識別子を使用して接続を正常に確立するスティミュラスコントローラーを使用して接続を受け入れていることを確認したため、私の仮定はそれが問題であるということです景色。また、@image インスタンス変数が関連するコントローラーから適切に設定されており、HTML で次の出力が生成されることも確認しました。

<turbo-cable-stream-source channel="ImageViewerChannel" data-image-id="14" signed-stream-name="ImltYWdlX3ZpZXdlcl8xNCI=--0eeda72e7a5478708717f1013f6a650e5b36c5f313a38a50a980e004a841b092"></turbo-cable-stream-source>

助けていただければ幸いです:)

編集:

さらなるデバッグを支援するために詳細を追加し、show.html.erb から他のすべてを削除して、次のようにしました。

<%= turbo_stream_from "image_viewer_#{@image.id}", channel: "ImageViewerChannel", data: {image_id: @image.id} %>
<%= tag.div id: :connected_users do %>
  <%= render "images/connected_users", count: 0 %>
<% end %>

コントローラ:

  def show
    @image = Image.find(params[:id])
  end

完全な HTML 出力:

<main class="container mx-auto mt-28 px-5 flex">
  <turbo-cable-stream-source channel="ImageViewerChannel" data-image-id="15" signed-stream-name="ImltYWdlX3ZpZXdlcl8xNSI=--b6210244563c1b1182cf3b15f63f8425aae5eb267eb38660507bae79fb80e1ca"></turbo-cable-stream-source>
  <div id="connected_users">
    <p> Medcial Image Viewer - Connected Users: 0 </p>
  </div>
</main>

アプリのログ:

Started GET "/images/15" for 127.0.0.1 at 2024-03-01 16:31:51 +0500
Processing by ImagesController#show as HTML
  Parameters: {"id"=>"15"}
  Image Load (0.1ms)  SELECT "images".* FROM "images" WHERE "images"."id" = ? LIMIT ?  [["id", 15], ["LIMIT", 1]]
  ↳ app/controllers/images_controller.rb:28:in `set_image'
  CACHE Image Load (0.0ms)  SELECT "images".* FROM "images" WHERE "images"."id" = ? LIMIT ?  [["id", 15], ["LIMIT", 1]]
  ↳ app/controllers/images_controller.rb:22:in `show'
  Rendering layout layouts/application.html.erb
  Rendering images/show.html.erb within layouts/application
  Rendered images/_connected_users.erb (Duration: 0.0ms | Allocations: 14)
  Rendered images/show.html.erb within layouts/application (Duration: 0.3ms | Allocations: 244)
  Rendered layout layouts/application.html.erb (Duration: 6.3ms | Allocations: 6107)
Completed 200 OK in 8ms (Views: 6.6ms | ActiveRecord: 0.1ms | Allocations: 7407)

Redis が実行されています:

brew services                                                                                           main * ] 4:30 pm
Name          Status     User        File
postgresql@14 error  256 xyz ~/Library/LaunchAgents/[email protected]
redis         started   xyz  ~/Library/LaunchAgents/homebrew.mxcl.redis.plist

解決策

いくつか気になる点があります。独自のターボ ストリーム チャネルを設定しましたが、依然として Turbo::StreamsChannel を通じて更新を送信しています。あなたは更新を受信するためにconnected_usersを購読していません。 Broadcast_replace_to はターボ フレームを置き換えます。つまり、最初の更新後はターゲットが存在しません。ターボ ストリームが機能するためにターボ フレームは必要ありません。

class ImageViewerChannel < ApplicationCable::Channel
  include Turbo::Streams::Broadcasts
  include Turbo::Streams::StreamName

  @@connected_users_count = 0

  def subscribed
    @@connected_users_count += 1
    @image_id = params[:image_id]
    stream_from "image_viewer_#{@image_id}"
    broadcast_connected_users_count
  end

  private

    def broadcast_connected_users_count
      # NOTE: if you `extend Turbo::Streams::Broadcasts` then you have to 
      #       call it as a class method `ImageViewerChannel.broadcast_update_to`
      broadcast_update_to(
        "image_viewer_#{@image_id}",     # <= this is where you're streaming from
                                         #    that's where the broadcast goes
        target: "connected_users",
        partial: "images/connected_users",
        locals: {count: @@connected_users_count}
      )
    end
end
<%= turbo_stream_from "image_viewer_#{@image.id}", channel: "ImageViewerChannel", data: {image_id: @image.id} %>

<%= tag.div id: :connected_users do %>
  <%= render "images/connected_users", count: 0 %>
<% end %>

Turbo::StreamsChannel から継承することもできると思います。

class ImageViewerChannel < Turbo::StreamsChannel
  @@connected_users_count = 0

  def subscribed
    super
    @@connected_users_count += 1
    broadcast_connected_users_count
  end

  private

    def broadcast_connected_users_count
      ImageViewerChannel.broadcast_update_to(
        verified_stream_name_from_params,
        target: "connected_users",
        partial: "images/connected_users",
        locals: {count: @@connected_users_count}
      )
    end
end

アップデート

これが私が得たものです:

<turbo-cable-stream-source channel="ImageViewerChannel" data-image-id="1" signed-stream-name="ImltYWdlX3ZpZXdlcl8xIg==--e5b0cbf8cc93373099c4c70b75fe8e397096882718b43d3638649d1da1c225ee" connected=""></turbo-cable-stream-source>

<div id="connected_users">
  <div> 2 </div>
</div>
  Rendered images/_connected_users.html.erb (Duration: 0.1ms | Allocations: 10)
ImageViewerChannel is streaming from image_viewer_1
[ActionCable] Broadcasting to image_viewer_1: "<turbo-stream action=\"update\" target=\"connected_users\"><template><div> 2 </div>\n</template></turbo-stream>"
ImageViewerChannel is transmitting the subscription confirmation
ImageViewerChannel transmitting "<turbo-stream action=\"update\" target=\"connected_users\"><template><div> 2 </div>\n</template></turbo-stream>" (via streamed from image_viewer_1)
ImageViewerChannel transmitting "<turbo-stream action=\"update\" target=\"connected_users\"><template><div> 2 </div>\n</template></turbo-stream>" (via streamed from image_viewer_1)

そしてケーブルメッセージ:

{
  "identifier": "{\"channel\":\"ImageViewerChannel\",\"signed_stream_name\":\"ImltYWdlX3ZpZXdlcl8xIg==--e5b0cbf8cc93373099c4c70b75fe8e397096882718b43d3638649d1da1c225ee\",\"image_id\":\"1\"}",
  "message": "<turbo-stream action=\"update\" target=\"connected_users\"><template><div> 2 </div>\n</template></turbo-stream>"
}