【问题标题】:RoR: Simple no refresh on button pushRoR:按钮按下时简单无刷新
【发布时间】:2020-07-31 11:31:56
【问题描述】:

我的应用程序上有用户关注和取消关注按钮。我不想做任何花哨的事情,我只是不想每次点击关注或取消关注按钮时都刷新页面。

我的控制器

relationships_controller.rb

     def create
        current_user.follow(@user)
        respond_to do |format|
         format.html { #handle HTML, i.e. full page reload }
         format.js # handle ajax request
        end
      end
    
      def destroy
        current_user.unfollow(@user)
        respond_to do |format|
          format.html
          format.js # this one handle the request comes from `remote: true` button
        end
      end

我的看法

tweets/index.html.erb

    <% if current_user.id != tweet.user.id %> 
                <% if current_user.following?(tweet.user) %>
                  <%= button_to "Unfollow", relationships_path(user_id: tweet.user), remote: true, method: :delete, :class => "btn btn-primary" %>
                 <% else %>
                   <%= button_to "Follow", relationships_path(user_id: tweet.user), remote: true, :class => "btn btn-primary" %>
                <% end %>
                <br>
               <% end %>
               <hr/>
              <% end %>

关系模型

relationship.rb

class Relationship < ApplicationRecord
    belongs_to :follower, class_name: "User"
    belongs_to :followed, class_name: "User"

    validates :follower_id, presence: true
    validates :followed_id, presence: true
end

用户模型

User.rb

has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
  has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy


  has_many :following, through: :active_relationships, source: :followed
  has_many :followers, through: :passive_relationships, source: :follower


  def follow(user)
    active_relationships.create(followed_id: user.id)
  end

  def unfollow(user)
    active_relationships.find_by(followed_id: user.id).destroy
  end

  def following?(user)
    following.include?(user)
  end

路线

routes.rb
    resource :relationships, :only => [:create, :destroy]

应用程序.js

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("chartkick")
require("chart.js")

//= require jquery3
//= require popper
//= require bootstrap-sprockets

检查过的按钮元素

<form class="button_to" method="post" action="/relationships?user_id=1" data-remote="true"><input class="btn btn-primary" type="submit" value="Follow"><input type="hidden" name="authenticity_token" value="hfwF8wXBcp/OM2P/pCYBnEBrjw22BDKWbw/dZFwwDsRpiIFq5jBKS/AoTMjkCZRrGum7UyW1kaL3h/4XEM2wIg=="></form>

当我点击跟随时,现在什么都没有发生。我想我的视图中需要一个新的 js 文件,但不确定如何实现它。

我已经研究过解决方案,但它们种类繁多,并且试图做的比我想做的更多,这只是简单的不刷新。

如何最好地做到这一点? (如果需要可以提供更多代码)

编辑:这段代码让 jQuery 在我的 ..webpack/enironment.js 文件中的 Rails 6 中工作

# app/config/webpack/environment.js
const {environment} = require('@rails/webpacker');

const webpack = require('webpack');
environment.plugins.append('Provide', new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery' # or if its not work specify path `'jquery/src/jquery'` which node_modules path for jquery
}));

module.exports = environment;

对于@max

  $(document).on('ajax:success', '.follow-btn', function(event){
  let $el = $(this);
  let method = this.dataset.method;
  if (method === 'post') {
    $('.follow-btn[href="'+this.href+'"]').each(function(el){ $(el).text('Unfollow');  });
    this.dataset.method = 'delete';
  } else if (method === 'delete') {
    $('.follow-btn[href="'+this.href+'"]').each(function(el){ $(el).text('Follow');  });
    this.dataset.method = 'post';
  }
});

【问题讨论】:

  • 您是否尝试过检查浏览器中的&lt;button&gt; 元素以确保设置了data-remote="true" 属性?另外,你能发布你的javascript清单吗?您需要使用jqueryujs 才能使远程选项生效。
  • 将两者都添加到问题中。 'data-remote="true"' 似乎已设置
  • 好的,现在它实际上正在工作,但我希望关注按钮更改为取消关注按钮点击(参见我上面的视图)。现在视图中没有任何反应。如果我刷新取消关注按钮,虽然这意味着关注操作有效,但我需要它只是更改为取消关注而不刷新
  • 您将不得不编写一些 JavaScript 来更改按钮的状态。这超出了您原始问题的范围,但您需要为相应的控制器操作创建 .js.erb 文件,并根据控制器的响应更改状态。有关系。
  • 好的,谢谢,这似乎比我想象的要复杂。稍后我会问一个新问题。

标签: javascript ruby-on-rails


【解决方案1】:

您可以发送一个 JSON 请求并编写一个简单的事件处理程序,而不是进入 js.erb 兔子洞。

让我们首先向按钮添加data-type="json" 属性,以便它们发送 JSON 请求而不是 javascript:

<% unless current_user == tweet.user %> 
  <% if current_user.following?(tweet.user) %>
    <%= link_to "Unfollow", relationships_path(user_id: tweet.user), 
        data: { remote: true, type: :json, method: :delete }, 
        class: "follow-btn btn btn-primary" 
  <% else %>
    <%= link_to "Follow", relationships_path(user_id: tweet.user), 
        data: { remote: true, type: :json, method: :post}, 
        class: "follow-btn btn btn-primary" 
    %>
  <% end %>
<% end %>

然后为您的控制器编写 JSON 响应。

def create
  current_user.follow(@user)
  respond_to do |format|
    format.html
    format.json { head :created }
  end
end
    
def destroy
  current_user.unfollow(@user)
  respond_to do |format|
    format.html
    format.json { head :no_content }
  end
end

你可以看到它非常简单,当创建一个资源时,你返回一个201 - Created,通常是一个位置标头或正文中的实体(一个描述创建内容的 JSON 有效负载)。当您更新或销毁记录时,204 - No Content 状态码就足够了。

如果您现在对其进行测试并查看浏览器检查器中的网络选项卡,您将看到发送了 AJAX 请求但视图中没有任何反应。

因此,让我们编写一个事件处理程序,在发送请求后切换按钮文本和方法。因为 Rails UJS 已经为我们创建了按钮的 AJAX 处理程序,所以我们可以 hook into its events:

// put this in your application.js or anywhere in your pack
$(document).on('ajax:success', '.follow-btn', function(event){
  let $el = $(this);
  let method = this.dataset.method;
  if (method === 'post') {
    $el.text('Unfollow');
    this.dataset.method = 'delete';
  } else if (method === 'delete') {
    $el.text('Follow');
    this.dataset.method = 'post';
  }
});

为什么这比 js.erb 模板更好?

  • 服务器端不参与更新客户端上的 UI。没有意大利面条代码视图。
  • JavaScript 被缩小,不是由 ERB 生成,易于调试/推理。
  • 可以更改为使用乐观创建/删除来提供即时反馈

【讨论】:

  • 这是一个缺少错误处理的简化示例 - 您应该预料到创建关系失败并相应调整的极端情况。
  • 谢谢。非常感激。我收到此错误 Uncaught ReferenceError: $ is not defined at Object../app/javascript/packs/application.js (application.js:28) at webpack_require (bootstrap:19) at bootstrap :83 在引导程序中:83
  • 搞定了,非常感谢。对于 Rails 6,我向 webpack/environment 添加了一些代码。 js。我希望它还可以在单​​击按钮时自动更新视图中的计数,但我想那是另一天 :) 谢谢。
  • 这是一个单独的问题,但您可以通过从 rails 的响应正文中返回一个计数(作为 JSON)并通过事件处理程序中的 'event.details[0]' 读取它并更改文本/元素。删除资源时使用 200 - OK。
  • 是的,选择所有要更改的元素并循环遍历它们并更改文本。 $('.follow-btn[href="'+this.href+'"]').each(function(el){ $(el).text('New text'); }); 之类的东西会更新指向同一用户的所有链接。在编写代码时,我实际上专门针对用户单击的元素以避免副作用。
猜你喜欢
  • 2016-11-15
  • 1970-01-01
  • 2012-09-26
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-02
  • 1970-01-01
相关资源
最近更新 更多