在 Ajax 请求后根据模型中的值更新 CSS class

Update CSS class based on value in model after Ajax request

我有一个显示剧集列表的视图。我为此使用了一部分:

    <div class="episode-list">
      <% if user_signed_in? %>
        <%= render partial: "shared_partials/episode_with_image_compact", collection: @recent_episodes, as: :episode %>
    <% end %>
    </div>

在该部分中,我有一个 button_to 向后端发送请求以将剧集标记为已看到:

          <%= button_to manual_new_scrobble_path, {method: :post, form_class: 'manual-scrobble-btn', class: "button is-small #{'listened' if episode.has_play}", remote: true, params: {scrobble: {user_id: current_user.id, episode_id: episode.id}}} do %>
            <%= scrobble_icon(episode) %>
          <% end %>

scrobble_icon 函数只是根据剧集的状态设置 class,如下所示:

  def scrobble_icon(episode)
    if episode.has_play
      icon('fas', 'check', class: 'listened')
    else
      icon('fas', 'check', class: 'unlistened')
    end
  end

我正在使用 remote: true,因此它不会导致页面加载。当我为 SPA 做其他前端框架时,默认情况下总是有这种双向绑定,其中值会根据 "store" / 模型的状态更新。

我现在已经阅读了多篇关于 TurboLinks and server generated javascript 的文章,但我仍然无法找出解决问题的正确方法。

我想要的:

实际发生了什么:

很多类似问题的回答都讲到jQuery也就是:

1) 不再是 Rails 6
2) 不是我想在 2019 年使用的东西

谢谢!

您需要呈现一个 javascript 脚本: 1 - 找到元素 2 - 更新为 class

首先我会添加一些标识符,以便您可以识别正确的按钮

<%= button_to manual_new_scrobble_path, {method: :post, form_class: 'manual-scrobble-btn', class: "button is-small #{'listened' if episode.has_play}", id: "button_for_episode_#{episode.id}", remote: true, params: {scrobble: {user_id: current_user.id, episode_id: episode.id}}} do %>
  <%= scrobble_icon(episode) %>
<% end %>

注意 `id: "button_for_episode_#{episode.id}" 参数

现在您可以使用类似这样的内容来定位它:

// find the button
let button = document.getElementById("button_for_episode_#{params[:scobble][:episode_id]}");
// add the class
button.classList.add('listened');

// find the icon within that button
let icon = button.querySelector('.fas');
// remove "unlistened" class
icon.classList.remove('unlistened');
// add "listened" class
icon.classList.add('listened');

此脚本应该是您正在调用的操作的视图。

最后我要做的是添加 class,如 arieljuod 的回答中所述,但我在之前的回复中不理解的构建块如下。

在您的 app/javascript/packs/application.js(如果您愿意,也可以自定义)中,您必须在 ajax:success 事件上设置一个侦听器,该事件由 Rails 发出,如 documentation

然后我们必须在事件中检查我们唯一的 id 以区分来自其他来源的事件。然后我们添加/删除 classes,一切都按预期工作。

到目前为止它似乎工作正常,如果我遗漏了什么请告诉我!

document.addEventListener('ajax:success', function (event) {
    let detail = event.detail;
    let data = detail[0], status = detail[1], xhr = detail[2];

    // Check if our event is from an manual episode scrobble button
    let button = [...event.target.children].find(element => element.id === 'scrobble_btn_episode' + data.episode_id);

    // If find() is not successful it's undefined
    if (typeof button !== 'undefined') {
        // We set the classes on our button
        button.classList.add("listened");
        button.classList.remove("unlistened");

        // The button has a child, we also set the classes there for the check mark
        let id = button.getElementsByClassName('fa-check');
        id[0].classList.add("listened");
        id[0].classList.remove("unlistened");
    }
});