欢迎来到cool的博客
7

Music box

Click to Start

点击头像播放音乐
新博客链接

gem 'closure_tree' 实作嵌套评论

效果图

 

 

gemfile
  gem 'closure_tree'

model
  class Event < ApplicationRecord
    has_many :comments, dependent: :destroy 
  end

  class Comment < ApplicationRecord
    belongs_to :user
    belongs_to :event
    validates :body, presence: true

    has_closure_tree order: "created_at DESC", dependent: :destroy
  end

  class User < ApplicationRecord
    has_many :comments
  end
  
route
  Rails.application.routes.draw do
    devise_for :users

    resources :events do
      resources :comments, except: [:show, :new, :index, :edit], controller: "event_comments"
    end

    root "events#index"

  end

views rails-recipes/app/views/events/show.html.erb
  <!-- Event Comment Section -->
  <%= render "event_comments/form" %>

  <!-- @event.comments index -->

  <%= comments_tree_for @comments %>
rails-recipes/app/views/event_comments/_form.html.erb
<div class="panel panel-comment">
  <div class="panel-body">
      <%= form_for @comment, url: event_comments_path(@event), method: :post do |f| %>

      <% from_reply_form ||= nil %>
      <% if from_reply_form %>
        <%= f.hidden_field :parent_id, value: parent.id %>
      <% else %>
        <%= f.hidden_field :parent_id, value: 0 %>
      <% end %>

      <div class="form-group">
        <%= f.text_area :body, class: "form-control" %>
      </div>

      <%= f.submit "Submit", class: "btn btn-primary btn-xs" %>
    <% end %>
  </div>
</div>
rails-recipes/app/views/comments/_comment.html.erb
<div class="well well-sm well-comment">
  <div class="tab-content">

    <div id="comment-<%= comment.id %>" class="tab-pane fade in active">
      <div class="media">
        <div class="media-left">
          <% if comment.user.logo.present? %>
          <%= image_tag(comment.user.logo.url(:thumb), class: "thumbnail thumbnail-sm") %>
          <% else %>
          <img src="<%= image_url 'default_user_logo.png' %>" alt="user logo" class: "thumbnail thumbnail-sm">
          <% end %>
        </div>
        <div class="media-body">
          <h4 class="media-heading"><%= comment.user.display_name %></h4>
          <p class="text-muted"><%= comment.created_at.to_s(:long) %></p>
          <p><%= simple_format comment.body %></p>
        </div>
      </div>
    </div>
    <div id="reply-<%= comment.id %>" class="tab-pane fade">
      <div class="media">
        <div class="media-left">
          <% if comment.user.logo.present? %>
          <%= image_tag(comment.user.logo.url(:thumb), class: "thumbnail thumbnail-sm") %>
          <% else %>
          <img src="<%= image_url 'default_user_logo.png' %>" alt="user logo" class: "thumbnail thumbnail-sm">
          <% end %>
        </div>
        <div class="media-body">
          <h4 class="media-heading">
            <%= comment.user.display_name %>
          </h4>
          <p class="text-muted"><%= comment.created_at.to_s(:long) %></p>
          <p><%= simple_format comment.body %></p>
        </div>
      </div>

      <!-- comment reply form -->
      <%= render "event_comments/form", from_reply_form: true, parent: comment %>

    </div>
    <div id="edit-<%= comment.id %>" class="tab-pane fade">
      <!-- <%= render "event_comments/form", form_edit_form: true %> -->
      <%= form_for comment, url: event_comment_path(@event,comment), method: :put do |f| %>
        <div class="form-group">
          <%= f.text_area :body, class: "form-control" %>
        </div>

        <%= f.submit "Submit", class: "btn btn-primary btn-xs" %>
      <% end %>
    </div>
  </div>

  <% if comment.leaf? %>
    <p><small class="text-muted text-success">还没有人回复--来互动一下!</small></p>
  <% end %>

  <ul class="nav nav-pills">
    <li class="active" ><a data-toggle="pill" href="#comment-<%= comment.id %>">show</a></li>
    <li><a data-toggle="pill" href="#reply-<%= comment.id %>">reply</a></li>
    <% if current_user && comment.user == current_user %>
      <li><a data-toggle="pill" href="#edit-<%= comment.id %>">edit</a></li>
      <li><%= link_to "delete", event_comment_path(@event,comment), method: :delete %></li>
    <% end %>
  </ul>
</div>
rails-recipes/app/controllers/events_controller.rb
  class EventsController < ApplicationController
    def show
      @event = Event.only_available.find_by_friendly_id!(params[:id])
      @comment = Comment.new
      @comments = @event.comments.hash_tree
    end
  end
  
rails-recipes/app/controllers/event_comments_controller.rb
class EventCommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :find_event

  def create
    if params[:comment][:parent_id].to_i > 0
      parent = @event.comments.find_by_id(params[:comment].delete(:parent_id))
      @comment = parent.children.build(comment_params)
    else
      @comment = @event.comments.new(comment_params)
    end
    @comment.user = current_user
    @comment.event = @event

    if @comment.save

      flash[:notice] = "comment 新建成功"
    end
    redirect_to event_path(@event)
  end

  def update
    @comment = @event.comments.find(params[:id])

    if @comment.update(comment_params)
      flash[:notice] = "comment 修改成功"
    end
    redirect_to event_path(@event)
  end

  def destroy
    @comment = @event.comments.find(params[:id])

    @comment.destroy
    flash[:alert] = "comment 已经删除"
    redirect_to event_path(@event)
  end

  private

  def find_event
    @event = Event.find_by_friendly_id(params[:event_id])
  end

  def comment_params
    params.require(:comment).permit(:body)
  end
end

rails-recipes/app/assets/stylesheets/event_comments.scss
    // styles for comment button
    .nav.nav-pills {
      li > a {
        color: red;
        padding: 0.4px 10px;
        margin-right: 10px;
        &:hover {
          background-color: #FFFFFF;
        }
      }
      li.active > a {
        background-color: #FFFFFF;
        border: 1px solid red;

        &:focus,

        &:hover {
          color: red;
          border: 1px solid red;

        }
      }
    }

    .panel-comment,

    .well-comment {
      background-image: image-url("/images/white_wall.png");
    }

    // comment layout
    .replies {
      margin-left: 50px;
    }
    .replies .replies .replies .replies .replies {
      margin-left: 0;
    }

几处重点

render @comments, render convention 约定,惯例;用reder 传递阐述

form_for url: new_event_comment_path(:parent_id), 重写 routing

f.hidden_field :parent_id, value: 0

params[:comment][:parent_id]

nil guard: 设定参数的默认值; from_reply_form ||= nil,

links:

https://www.sitepoint.com/nested-comments-rails/
https://github.com/ClosureTree/closure_tree#available-options

 

原文: http://chanweiyan.logdown.com/posts/2249568

返回列表