Rails 7 JavaScript事件听众在浏览器返回后停止工作吗?

发布于 2025-02-11 21:08:21 字数 3045 浏览 1 评论 0 原文

我有Rails 7应用程序,我正在尝试使用 tom-select 纤细 - 选择。无论我使用哪个库,我的问题都会重现,因此,这一定是我的铁路方面的问题。

app/views/cities/index.html.erb


<%= form_for :city, url: cities_path, method: 'GET' do |f| %>
  <div class="mt-4 border bg-light px-4 py-3 rounded-3">
    <%= f.select :search_city, [], {},
                  placeholder: 'Type to search',
                  data: {
                    controller: 'ts--search',
                    ts__search_url_value: autocomplete_cities_path
                  } %>
    <%= f.submit 'Search', class: 'btn mx-auto' %>
  </div>
<% end %>

,这是我的JS控制器(在这种情况下,我正在使用tom-select) app/javascript/controllers/ts/search_controller.js

import { Controller } from "@hotwired/stimulus";
import { get } from "@rails/request.js";
import TomSelect from "tom-select";

export default class extends Controller {
  static values = { url: String };

  connect() {
    var config = {
      plugins: ["input_autogrow", "remove_button", "no_active_items"],
      render: {
        option: this.render_option,
        item: this.render_option,
      },
      valueField: "value",
      loadThrottle: 400,
      load: (q, callback) => this.search(q, callback),

      closeAfterSelect: true,
      persist: false,
      create: false,
      delimiter: ", ",
      maxItems: 10,
    };

    new TomSelect(this.element, config);
  }

  async search(q, callback) {
    const response = await get(this.urlValue, {
      query: { query: q },
      responseKind: "json",
    });

    if (response.ok) {
      callback(await response.json);
    } else {
      console.log("Error in search_ctrl: ");
      callback();
    }
  }

  render_option(data, escape) {
    return `<div>${escape(data.text)}</div>`;
  }
}

app/controllers/controllers/cotities_controller.rb

class CitiesController < ApplicationController
  def index
  end

  def autocomplete
    list = City.order(:name)
               .where("name ilike :q", q: "%#{params[:q]}%")

    render json: list.map { |u| { text: u.name, value: u.id, sub: u.state } }
  end

end

问题repro repro :

  1. 开放城市索引并单击搜索酒吧。
  2. 下拉列表打开,我可以输入并选择一个建议。一旦选择,建议在搜索栏中出现“ x”单击的搜索栏,该建议将从搜索栏中删除它。
  3. 我添加任何数量的搜索令牌,1-N。
  4. 单击“搜索” - &GT;查看结果页面。
  5. 单击浏览器中的备用按钮(或在手机上滑动)

预期行为: 搜索栏与搜索之前完全一样。单击“ X”删除令牌。单击搜索栏允许输入搜索查询并添加更多令牌。

实际行为: 我可以看到令牌,但是除了“搜索”按钮之外,请单击其他任何内容,什么也不做。 我可以看到多个演示中的相同行为,例如一个 href =“ https://coolrequest.dev/2021/11/25/replace_select2.html” rel =“ nofollow noreferrer”>一个。

回来后如何使JS工作?

I have rails 7 app where I'm trying to build a search bar using either tom-select or slim-select. My issue reproduces no matter which library I'm using therefore it must be the issue on my rails side.

app/views/cities/index.html.erb


<%= form_for :city, url: cities_path, method: 'GET' do |f| %>
  <div class="mt-4 border bg-light px-4 py-3 rounded-3">
    <%= f.select :search_city, [], {},
                  placeholder: 'Type to search',
                  data: {
                    controller: 'ts--search',
                    ts__search_url_value: autocomplete_cities_path
                  } %>
    <%= f.submit 'Search', class: 'btn mx-auto' %>
  </div>
<% end %>

and this is my js controller (in this case I'm using tom-select)
app/javascript/controllers/ts/search_controller.js

import { Controller } from "@hotwired/stimulus";
import { get } from "@rails/request.js";
import TomSelect from "tom-select";

export default class extends Controller {
  static values = { url: String };

  connect() {
    var config = {
      plugins: ["input_autogrow", "remove_button", "no_active_items"],
      render: {
        option: this.render_option,
        item: this.render_option,
      },
      valueField: "value",
      loadThrottle: 400,
      load: (q, callback) => this.search(q, callback),

      closeAfterSelect: true,
      persist: false,
      create: false,
      delimiter: ", ",
      maxItems: 10,
    };

    new TomSelect(this.element, config);
  }

  async search(q, callback) {
    const response = await get(this.urlValue, {
      query: { query: q },
      responseKind: "json",
    });

    if (response.ok) {
      callback(await response.json);
    } else {
      console.log("Error in search_ctrl: ");
      callback();
    }
  }

  render_option(data, escape) {
    return `<div>${escape(data.text)}</div>`;
  }
}

app/controllers/cities_controller.rb

class CitiesController < ApplicationController
  def index
  end

  def autocomplete
    list = City.order(:name)
               .where("name ilike :q", q: "%#{params[:q]}%")

    render json: list.map { |u| { text: u.name, value: u.id, sub: u.state } }
  end

end

Problem Repro:

  1. Open cities index and click on the search bar.
  2. Dropdown opens up, I can type, and select a suggestion. Once selected, suggestion appears in the search bar with an 'x' clicking which will remove the it from the search bar.
  3. I add any amount of search tokens, 1-N.
  4. Click "Search" -> Seeing the results page.
  5. Click the back button in the browser (or swipe back on a phone)

Expected behavior:
The search bar is exactly as it was before the search. clicking on 'x' removes the token. clicking on the Search bar allows entering the search query and adding more tokens.

Actual behavior:
I can see the tokens, but clicking anything but the 'Search' button, does nothing.
I can see the same behavior across multiple demos like this one and this one.

How can i make the JS work after coming back?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

下雨或天晴 2025-02-18 21:08:21
// TLDR

// app/javascript/controllers/ts/search_controller.js
disconnect() {
  this.element.tomselect.destroy();
}

当使用浏览器“返回按钮”时访问并显示页面的缓存副本。在访问另一个页面之前,该副本就保存了。任何附带的JavaScript行为都会丢失,我们只会得到HTML。

刺激连接到 [data-controller = ts-search] select 元素由 tomSelect 从这样

<select placeholder="Type to search" data-controller="ts--search" data-ts--search-url-value="/cities/autocomplete" name="city[search_city]" id="city_search_city">
</select>

做:

<select placeholder="Type to search" data-controller="ts--search" data-ts--search-url-value="/cities/autocomplete" name="city[search_city]" id="city_search_city"
  multiple="multiple"
  tabindex="-1"
  class="tomselected ts-hidden-accessible">
<!--     ^
  NOTE: this class
-->
</select>

<div class="ts-wrapper multi plugin-input_autogrow plugin-remove_button plugin-no_active_items has-options">
   <!-- ... -->
</div>

当单击另一个链接时,此修改后的HTML保存到缓存中,然后在使用浏览器后退导航时恢复后来还原。然后,刺激再次连接,但是, tomSelect skips .tomselected 元素,以避免再次附加 .ts-wrapper 。它看起来相同,因为HTML和样式已加载,但没有附加JavaScript行为。

我们可以通过打开 impoulus 调试记录来获得更多上下文:

// app/javascript/controllers/application.js
application.debug = true // <= set this to `true`

// app/javascript/controllers/ts/search_controller.js
// inside connect()
console.log(this.element.getAttribute("class"));
new TomSelect(this.element, config);
console.log(this.element.getAttribute("class"));

如果带有搜索表格的页面被缓存,并且我们通过单击链接来导航到它:

                                  // a cached page is displayed while
                                  // waiting for response from the server

ts--search #initialize            // found ts--search on the page
tomselected ts-hidden-accessible  // cached <select>
                                  // new TomSelect() has no effect
tomselected ts-hidden-accessible  // at least it looks the same
ts--search #connect               // finished connecting

                                  // a fresh response from the server arrived

ts--search #disconnect            // <= SOLUTION
ts--search #initialize            // run the lifecycle again on a new page
null                              // untouched <select> from the server
                                  // new TomSelect() now works
tomselected ts-hidden-accessible  // new fancy select is on the page
ts--search #connect               // done

使用浏览器后返回导航时:

                                  // a cached page is displayed

ts--search #initialize            // found ts--search on the page
tomselected ts-hidden-accessible  // cached <select>
tomselected ts-hidden-accessible  // new TomSelect() does nothing
ts--search #connect               // fail

还有一件事发生。从我们的表单中导航时(通过单击“浏览器”或“向前浏览器”向前):

before-cache
ts--search #disconnect

在该页面被 turbo signulus 调用 disconnect()在我们的搜索控制器中。我们可以在此处重新恢复原始选择,然后在涡轮增压之前。这样,可以在缓存的页面上重新应用TomSelect TomSelect

// app/javascript/controllers/ts/search_controller.js

disconnect() {
  this.element.tomselect.destroy();
}

https://turbo.hotwired.dev/

https://turbo.hotbo.hotwired.dev /手册/建筑物#理解-Caching

https://tom-select.js.org/docs/api/#destroy

// TLDR

// app/javascript/controllers/ts/search_controller.js
disconnect() {
  this.element.tomselect.destroy();
}

When browser "back button" is used Turbo Drive does a restoration visit and displays a cached copy of the page. This copy is saved just before visiting another page. Any attached javascript behavior is lost, we only get html.

When Stimulus connects to [data-controller=ts--search] the select element is modified by TomSelect from this:

<select placeholder="Type to search" data-controller="ts--search" data-ts--search-url-value="/cities/autocomplete" name="city[search_city]" id="city_search_city">
</select>

to this:

<select placeholder="Type to search" data-controller="ts--search" data-ts--search-url-value="/cities/autocomplete" name="city[search_city]" id="city_search_city"
  multiple="multiple"
  tabindex="-1"
  class="tomselected ts-hidden-accessible">
<!--     ^
  NOTE: this class
-->
</select>

<div class="ts-wrapper multi plugin-input_autogrow plugin-remove_button plugin-no_active_items has-options">
   <!-- ... -->
</div>

When clicking another link, this modified html is saved to cache and later is restored when using browser back navigation. Then Stimulus connects again, however, TomSelect skips .tomselected elements to avoid appending .ts-wrapper again. It looks the same because html and styles are loaded, but no javascript behavior is attached.

We can get a bit more context by turning on Stimulus debug logging:

// app/javascript/controllers/application.js
application.debug = true // <= set this to `true`

// app/javascript/controllers/ts/search_controller.js
// inside connect()
console.log(this.element.getAttribute("class"));
new TomSelect(this.element, config);
console.log(this.element.getAttribute("class"));

If the page with the search form is cached and we navigate to it by clicking a link:

                                  // a cached page is displayed while
                                  // waiting for response from the server

ts--search #initialize            // found ts--search on the page
tomselected ts-hidden-accessible  // cached <select>
                                  // new TomSelect() has no effect
tomselected ts-hidden-accessible  // at least it looks the same
ts--search #connect               // finished connecting

                                  // a fresh response from the server arrived

ts--search #disconnect            // <= SOLUTION
ts--search #initialize            // run the lifecycle again on a new page
null                              // untouched <select> from the server
                                  // new TomSelect() now works
tomselected ts-hidden-accessible  // new fancy select is on the page
ts--search #connect               // done

When using browser back navigation:

                                  // a cached page is displayed

ts--search #initialize            // found ts--search on the page
tomselected ts-hidden-accessible  // cached <select>
tomselected ts-hidden-accessible  // new TomSelect() does nothing
ts--search #connect               // fail

One more thing happens when navigating away from our form (by clicking away, browser back or browser forward):

before-cache
ts--search #disconnect

Before the page is cached by Turbo, Stimulus calls disconnect() in our search controller. We can restore the original select here, before turbo caches the page. This way TomSelect can be reapplied on the cached page.

// app/javascript/controllers/ts/search_controller.js

disconnect() {
  this.element.tomselect.destroy();
}

https://turbo.hotwired.dev/handbook/drive#restoration-visits

https://turbo.hotwired.dev/handbook/building#understanding-caching

https://stimulus.hotwired.dev/reference/lifecycle-callbacks#disconnection

https://tom-select.js.org/docs/api/#destroy

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文