如何首先在broadcats_to中呈现部分,然后重定向(AbstractController :: DoubleRenderError)

时间:2018-09-14 01:49:26

标签: ruby-on-rails ruby actioncable

我是Rails的新手,正尝试使用Action Cable在数据库中保存新记录时更新客户端。

为此,我有这个posts_controller.rb

  # POST /posts
  # POST /posts.json
  def create
    @post = Post.new(post_params)
    picture = params[:post][:picture]
    respond_to do |format|
      if @post.save
        @post.picture.attach(picture) if picture.present?
        PostsChannel.broadcast_to @post, html: render(partial: 'post', locals: { post: @post }) #<--- render
        format.html { redirect_to posts_url, notice: 'Post was successfully created.' } #<--- redirect
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

还有_post.html.erb

<div class="cd-timeline__block js-cd-block">
  <div class="cd-timeline__img cd-timeline__img--picture js-cd-img">
    <p></p>
  </div>

  <div class="cd-timeline__content js-cd-content">
    <%= image_tag post.picture.variant(resize: '400x400') %>
    <p><%= post.text %></p>
    <span class="cd-timeline__date"><%= post.created_at.strftime('%d/%m/%Y %T') %></span>
  </div>
</div>

由于部分渲染和重定向,最终出现错误AbstractController::DoubleRenderError。问题是我不知道该如何做。我不想在JS文件中生成HTML,因为看到了一些代码示例(我的帖子模板将有代码重复)。我一直在寻找其他示例,但没有任何帮助。

herehere讨论了相同的问题,但是我看不到如何使用Flash获得我想要的东西。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

您可以尝试将渲染移至after_action回调吗?您将有权访问@post,因此可以致电@post.valid?来了解是否必须广播该广播。我不确定是否可行,但是您可以尝试一下。

不过,我不会播放部分渲染图。也许将@post作为json广播并使用javascript客户端更新视图会更快。

编辑:尝试使用render_to_string https://apidock.com/rails/AbstractController/Rendering/render_to_string

它呈现视图和局部视图,但未设置请求的响应主体。刚刚在本地尝试过,它可用于两个渲染器。

答案 1 :(得分:0)

好,所以我知道了! :)

有一个Rails函数可以完全满足我的需求:render_to_string不会发送http响应。 我在websockets上遇到了一些问题,因此我安装了Redis +更改了广播方式,现在一切正常!

记录如下:

# config/routes.rb
Rails.application.routes.draw do
  resources :posts
  mount ActionCable.server => '/cable'
end

-

# config/cable.yml
development:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>

-

# app/channels/posts_channel.rb
class PostsChannel < ApplicationCable::Channel
  def subscribed
    stream_for 'post'
  end
end

-

// app/assets/javascripts/channels/posts.js
App.cable.subscriptions.create('PostsChannel', {
  received: function({ html }) {
    $("#timeline-container").prepend(html);
  }
});

-

<!-- app/views/posts/_post.html.erb -->
<div class="cd-timeline__block js-cd-block">
  <div class="cd-timeline__img cd-timeline__img--picture js-cd-img">
    <p></p>
  </div>

  <div class="cd-timeline__content js-cd-content">
    <%= image_tag post.picture.variant(resize: '400x400') %>
    <p><%= post.text %></p>
    <span class="cd-timeline__date"><%= post.created_at.strftime('%d/%m/%Y %T') %></span>
  </div>
</div>

-

# POST /posts
# POST /posts.json
def create
  @post = Post.new(post_params)
  picture = params[:post][:picture]
  respond_to do |format|
    if @post.save
      @post.picture.attach(picture) if picture.present?
      ActionCable.server.broadcast 'posts:post', html: render_to_string(partial: 'post', locals: { post: @post })
      format.html { redirect_to posts_url, notice: 'Post was successfully created.' }
      format.json { render :show, status: :created, location: @post }
    else
      format.html { render :new }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end