Hướng dẫn tạo chức năng comment bằng Gem private_pub
Bài đăng này đã không được cập nhật trong 3 năm
1.Giới thiệu
Trong bài viết này tôi sẽ hướng dẫn tạo 1 web đơn giản với chức năng tạo status và comment. Sử dụng gem private_pub để có thể hiển thị comment khi có comment mới của mình hoặc của người khác comment từ nơi khác mà không cần load lại trang.
Trước khi làm những hướng dẫn dưới đây, bận cần làm trước việc tạo user và đăng ký, đăng nhập bằng devise. Có thể xem hướng dẫn tại: https://github.com/plataformatec/devise
2.Cài đặt Gem
Thêm vào Gemfile
gem “private_pub”
gem “thin”
Chạy
$ bundle install
Sau đó chạy lệnh
$ rails g private_pub:install
để tạo file init cho private_pub
3.Tạo model
Tạo model Status
$ ails generate model Status content:text user:references
Tạo model StatusComment
$ rails generate model StatusComment content:text user:references status:references
Thêm và sửa các file model:
File user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :statuses, dependent: :destroy
has_many :status_comments, dependent: :destroy
end
File status.rb
class Status < ActiveRecord::Base
belongs_to :user
has_many :status_comments, dependent: :destroy
accepts_nested_attributes_for :status_comments, allow_destroy: :true
default_scope -> {order(created_at: :desc)}
validates :user_id, presence: true
validates :content, presence: true, length: {maximum: 2000}
end
File status_comment.rb
class StatusComment < ActiveRecord::Base
belongs_to :user
belongs_to :status
validates :user_id, presence: true
validates :content, presence: true, length: {maximum: 140}
end
4. Tạo controller và view
File config/routes.rb
Rails.application.routes.draw do
root "statuses#index"
devise_for :users
devise_scope :user do
get "sign_out", to: "devise/sessions#destroy"
get "sign_in", to: "devise/sessions#new"
get "sign_up", to: "devise/registrations#new"
end
resources :statuses
resources :status_comments
end
File statuses_controller.rb
class StatusesController < ApplicationController
respond_to :html, :json
def index
@statuses = Status.all
@status = Status.new
end
def create
@status = current_user.statuses.build status_params
if @status.save
flash[:success] = "Status created!"
redirect_to root_url
else
@feed_items = []
render "static_pages/home"
end
end
def destroy
@status.destroy
flash[:success] = "Status deleted"
redirect_to request.referrer || root_url
end
private
def status_params
params.require(:status).permit :content
end
end
status_comments_controller.rb
class StatusCommentsController < ApplicationController
respond_to :html, :json
def create
@status_comment = StatusComment.create! status_comment_params
respond_to do |format|
format.html { }
format.js
end
end
def destroy
@status_comment = StatusComment.find params[:id]
@status_comment.destroy
redirect_to :back
end
private
def status_comment_params
params.require(:status_comment).permit :status_id, :user_id, :content
end
end
Trang index của status index.html.erb
User: <%= current_user.name %>
<%= link_to "Sign out", sign_out_path %></br>
----------------------------------------------------------
</br>
<%= form_for @status, html: {multipart: true} do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new status...", class: "form-control" %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
<%= render @statuses %>
File views/statuses/_status.html.erb
<li id="<%= status.id %>">
<span class="user">
<%= status.user.name %>
</span>
<br>
<span class="timestamp text-success">
<%= "Posted #{time_ago_in_words(status.created_at)} ago." %>
<% if current_user == status.user %>
<%= link_to "Delete", status, method: :delete, data: {confirm: "You sure?"} %>
<% end %>
</span></br>
<span class="content">
<%= status.content %></br>
</span></br>
Comments:</br>
<% @status_comments = status.status_comments %>
<span id="status-comment<%= status.id %>">
<%= render @status_comments %>
</span>
<span id="form-status">
<%= form_for StatusComment.new , remote: true do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.hidden_field :status_id, value: status.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_area :content, placeholder: "Compose new comment...", class: "form-control" %>
</div>
<%= f.submit "Comment", class: "btn btn-primary" %>
<% end %>
</span>
</li>
</br>
File views/status_comments/_status_comment.html.erb
<li>
<% if status_comment.id %>
<%= status_comment.user.name %>:
<%= status_comment.content %></br>
<%= time_ago_in_words(status_comment.created_at) %>
<%= link_to "Delete", status_comment_path(status_comment), method: :delete, data: {confirm: "You sure?"} if current_user == status_comment.user%>
</br></br>
<% end %>
</li>
4.Sử dụng private_pub
Thêm vào file application.js
//= require private_pub
Thêm file private_pub.js và add vào layout
<%= javascript_include_tag "private_pub" %>
Thêm vào file config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( private_pub.js )
Chạy Faye.
$ rackup private_pub.ru -s thin -E production
Dùng subscribe_to trong trang mà minh muốn bắt thay đổi để subscribe một kênh.
<%= subscribe_to "/comments/new" %>
Dùng publish_to để gửi JS đến kênh đó. Ta sẽ viết trong file create.js.erb và để trong thư mục views/status_comments/
<% publish_to "/comments/new" do %>
$("#status-comment<%= @status_comment.status_id %>").append("<%= j render(@status_comment) %>");
<% end %>
Thêm
$("#form-status form textarea").val("");
để xóa text khi đã comment xong.
Như vậy ta đã hoàn thành. Chạy rails s và thử kết quả.
Ứng dụng chạy thử trên heroku: https://protected-plateau-5161.herokuapp.com/
5.Tài liệu tham khảo
All rights reserved