Together网站搭建之踩坑系列2

写在前面

打算后面写一些功能实现方面的小文章,详见目录。

完整作品源码及效果,看这里作品网站, 作品源码

今天先来一个收藏功能+Ajax优化.

目录

正文

先上效果图:

咱分两步走:

Step1、制作收藏功能
  • 新建一个favorite model,终端运行:

    rails g model favorite user_id:integer event_id:integer
    rake db:migrate
    
  • 建立favorite与user,event的关系

    • 在app/models/favorite.rb中,加入:

        belongs_to :user
        belongs_to :event
      
    • app/models/event.rb中,加入:

      has_many :favorites
      has_many :fans, through: :favorites, source: :user
      
    • app/models/user.rb中,加入:

      has_many :favorites
      has_many :favorite_events, through: :favorites, source: :event
      
    • app/models/user.rb中,加入是否已收藏活动的判断,及收藏与取消收藏的method:

        def is_favorite_of?(event)
          favorite_events.include?(event)
        end
      
        def favorite!(event)
          favorite_events << event
        end
      
        def unfavorite!(event)
          favorite_events.delete(event)
        end
      
  • 修改config/routes.rb中events 部分:

      resources :events do
        put :favorite, on: :member
      end
    
  • 在需要添加收藏的页面,比如event的show或者index,加上收藏及取消收藏,代码以index页面为例:

    【这里,建议你安装font-awesome-rails这个gem,如果没有,也没关系,用button代替即可】

    装了font-awesome-rails,可以这么来:

    <% @events.each do |event| %>
    .......
    <% if current_user && current_user.is_favorite_of?(event)%>
        <%= link_to favorite_event_path(event,type: "unfavorite"), method: :put do %>
        <i class="fa fa-star"> 已收藏 </i>
        <% end %>
    <% else %>
        <%= link_to favorite_event_path(event,type: "favorite"), method: :put do %>
        <i class="fa fa-star-o"> 收藏</i>
        <% end %>
    <% end %>
    .......
    

    没有装,咱可以这么来:

    <% @events.each do |event| %>
    .......
    <% if current_user && current_user.is_favorite_of?(event)%>
        <%= link_to('已收藏',favorite_event_path(event,type: "unfavorite"), method: :put, class: "btn btn-default") %>
    <% else %>
        <%= link_to('收藏',favorite_event_path(event,type: "favorite"), method: :put, class: "btn btn-success") %>
    <% end %>
    .......
    
  • 编辑app/controller/events_controller.rb,加上favorite方法,同时在before_action中加上favorite

    before_action :authenticate_user!, only: [:favorite, .....]
    
    .....
    
      def favorite
        @event = Event.find(params[:id])
        type = params[:type]
        if type == "favorite"
          current_user.favorite!(@event)
        else type == "unfavorite"
          current_user.unfavorite!(@event)
        end
        redirect_to :back
      end
    
      ......
    

    到这里,功能基本OK了,下面我们请上Ajax.

Step2、Ajax优化

目标:实现快速收藏,同时显示收藏人数

  • 编辑app/views/events/index.html.erb,把原来用作收藏的html部分删除,放入partial中:

    <% @events.each do |event| %>
    .......
    -  <% if current_user && current_user.is_favorite_of?(event)%>
    -     <%= link_to('已收藏',favorite_event_path(event,type: "unfavorite"), method: :put, class: "btn btn-default") %>
    -  <% else %>
    -     <%= link_to('收藏',favorite_event_path(event,type: "favorite"), method: :put, class: "btn btn-success") %>
    - <% end %>
    
    +    <div id="favorite-<%= event.id %>">
    +     <%= render :partial => "favorite", :locals => {:event => event }%>
    +   </div>
    
    .......
    
  • 新建app/views/events/_favorite.html.erb, 添加如下代码到_favorite.html.erb中:

    <% if current_user && current_user.is_favorite_of?(event) %>
        <%= link_to favorite_event_path(event,type: "unfavorite"), method: :put, :remote => true do %>
        <i class="fa fa-star"> 已收藏 (<span id="favorite-counts-<%= event.id %>"><%= event.favorites.count%></span>)</i>
        <% end %>
    <% else %>
        <%= link_to favorite_event_path(event,type: "favorite"), method: :put, :remote => true do %>
        <i class="fa fa-star-o"> 收藏 (<span id="favorite-counts-<%= event.id %>"><%= event.favorites.count%></span>)</i>
        <% end %>
    <% end %>
    

    【这里默认已安装了font-awesome-rails这个gem】

  • 编辑app/controllers/events_controller.rb, 去掉favorite中的redirect_to:

    .....
    
      def favorite
        @event = Event.find(params[:id])
        type = params[:type]
        if type == "favorite"
          current_user.favorite!(@event)
        else type == "unfavorite"
          current_user.unfavorite!(@event)
        end
      - redirect_to :back
      end
    
      ......
    
  • 新建app/views/events/favorite.js.erb,添加如下代码到favorite.js.erb中:

    str = "<%=j render :partial => "favorite", :locals => {:event => @event } %>";
    $("#favorite-<%= @event.id %>").html(str);
    $("#favorite-counts-<%= @event.id %>").html("<%= @event.favorites.count %>");
    

啦啦啦,大功告成!!

参考文章:如何做出收藏工作的功能?

稍稍加个餐:

BTW,简单提一个在用 Ajax 优化时,遇到的bug。

在未登录的情况,用户进行点赞及收藏,应该跳出登录界面,但网站没有任何反应,而你的controller里面已经有了before_action :authenticate_user!, 貌似没起效果,怎么破?

上图:

附上服务器报错信息:

Google后,会发现一堆相关的,包括在config/initializers/devise设置config.http_authenticatable_on_xhr = false, 修改routes.rb的,设置CSRF token的……试了好些个,没能解决。比较好笑的是,最后找到的解决办法,有些过于简单,哈哈。
附上:

app/controllers/events_controller.rb中,添加如下代码:

class EventsController < ApplicationController
  before_action :check_user, only: [:favorite, :like]

  ......
  def check_user
  return if user_signed_in?
  redirect_to new_user_session_path, error: '请先登录'
 end
......
end

现在点击收藏或点赞,会跳转到登录页面,OK!

参考:rails 4- 401 Unauthorized error handling