Каким способом Hotwire справиться с отключением данных?
Я начинаю использовать Hotwire в своем приложении Rails и реализовал нечто похожее на обучающее видео, в котором форма отправляется и обновляет другую часть страницы. Как и в видео, мне пришлось реализовать собственный контроллер формы сброса:
import {Controller} from "stimulus"
export default class extends Controller {
reset() {
this.element.reset()
}
}
для сброса значений в форме. Форма выглядит примерно так:
<%= form_with(model: @invitation,
data: {controller: "reset-form",
action: "turbo:submit-end->reset-form#reset"}) do |form| %>
<!-- other form elements -->
<%= form.submit "Invite" %>
<% end %>
который генерируется в такой кнопке:
<input type="submit" name="commit" value="Invite" data-disable-with="Invite">
Потому что, когда вы нажимаете кнопку, она отключается, чтобы избежать отправки по двойному щелчку, и это здорово. Проблема в том, что
this.element.reset()
не сбрасывает его.
Как правильно с этим справиться?
Я не ищу обходного пути, я знаю многих, но я ищу чистое решение этой проблемы.
Это отключение кнопки вызвано UJS? Означает ли это, что UJS не следует использовать в приложении Stimulus?
Я могу снова включить кнопку из
reset
Функция JavaScript, но если кнопка ввода указана следующим образом:
<%= form.submit "Invite", data: {disable_with: "Inviting, please wait..."} %>
затем исходная метка (
value
) кнопки потеряно, и у меня нет способа восстановить ее, что заставляет меня думать, что все, что реализует эту функциональность (UJS?), не предназначено для приложений hotwire / spa и ожидает полной перезагрузки страницы .
Я мог просто бросить:
config.action_view.automatically_disable_submit_tag = false
и реализовал мою собственную защиту от двойных щелчков с помощью контроллера Stimulus, но это неправильно. Проблема не в атрибуте
data-disable-with
но как это было реализовано.
5 ответов
Turbo уже давно отключил кнопку отправки и добавил еесовсем недавно. data-turbo-submits-with
как альтернатива UJS :
# available since: turbo v7.3.0 (turbo-rails v1.4.0) thx @PaulOdeon
<%= form_with model: @invitation do |form| %>
<%= form.submit "Invite", data: {turbo_submits_with: "Inviting..."} %>
<% end %>
Сброс формы не является обязательным:
<%= form_with model: @invitation, data: {
controller: "reset-form",
action: "turbo:submit-end->reset-form#reset"
} do |form| %>
<%= form.submit "Invite", data: {turbo_submits_with: "Inviting..."} %>
<% end %>
# NOTE: form reset is implemented as stimulus controller in the question
https://turbo.hotwired.dev/reference/attributes
Да, вам нужно просто добавить это:
config.action_view.automatically_disable_submit_tag = false
это только добавляетdata-disable-with
атрибут, который является специфичным для UJS и не влияет на Turbo.
Однако это не значит, что раньше это было невозможно. Это упрощенный пример, если вы хотите реализовать собственное поведение. Чтобы провести здесь четкое различие, я использую атрибуты, не относящиеся к Turbo, и атрибуты, не относящиеся к ujs (Turbo только устанавливает и удаляетdisabled
атрибут для нас):
<%= form_with model: @invitation, data: {reset: true} do |form| %>
<%= form.submit "Invite" data: {disable_append: "..."} %>
<% end %>
Для простых одноразовых действий вы можете пропустить Стимул и использовать только Турбо- события:
// app/javascript/application.js
document.addEventListener('turbo:submit-start', (event) => {
const { detail: { formSubmission, formSubmission: { submitter } } } = event
// disable-append or maybe disable-loading and make it spin
if (submitter.dataset.disableAppend) {
// TODO: handle <button> element and use innerHTML instead of value.
// save original text
formSubmission.originalText = submitter.value
submitter.value += submitter.dataset.disableAppend
}
})
document.addEventListener('turbo:submit-end', (event) => {
const { detail: { formSubmission, formSubmission: { formElement, submitter } } } = event
// put it back (formSubmission is the same object during form submission lifecycle)
if (formSubmission.originalText) {
submitter.value = formSubmission.originalText
}
// data-reset
if (formElement.dataset.reset === 'true') {
formElement.reset()
}
})
Примерно так это сейчас и делается в турбо:
https://github.com/hotwired/turbo/blob/v7.3.0/src/core/drive/form_submission.ts#L217-L239
TL;DR: добавьте в свой TurboFrame
Например:
<%= turbo_frame_tag @invitation, target: '_top' do %>
<!-- Your form here -->
<% end %>
Это обновит всю страницу, что является рекомендуемым рабочим процессом (см. ниже).
Полный ответ
Означает ли это, что UJS не следует использовать в приложении Stimulus?
Правильно. UJS не следует использовать с Hotwire, так как он официально объявлен устаревшим . Кнопка, отключающаяся при отправке пользователем, теперь является частью Hotwire. Вы можете безопасно удалить RailsUJS. При необходимости см. руководство по обновлению .
Отправка формы + обновление нескольких частей страницы
Есть два способа сделать это с помощью Hotwire:
- TurboFrames: Установите цель вверху (т.е.
target="_top"
) для повторного рендеринга всей страницы. Я очень рекомендую этот подход. Это то, что мы делаем. - TurboStreams: вернуть несколько TurboStreams. Обратите внимание, что, несмотря на другие ответы, вам вообще не нужны веб-сокеты. Это можно сделать с помощью запроса HTTP Post.
Использование TurboFrames (рекомендуется)
Пока самый простой способ. Оберните вашу форму тегом turbo_frame, что вы, вероятно, уже сделали, но нацельтесь на перезагрузку всей страницы, используя:
<%= turbo_frame_tag @invitation, target: '_top' do %>
<!-- Your form here -->
<% end %.
Обратите внимание, что по умолчанию Hotwire перезагружает только текущий TurboFrame. Добавление этого дополнительногоtarget: '_top'
перезагрузит всю страницу с помощью Hotwire, что хорошо и хорошо подтверждается его документацией .
Использование Турбофреймов
Этот метод намного сложнее. Короче говоря, вы бы:
- Оберните форму и другую часть страницы, которую вы хотите обновить,
turbo_stream
помощники. - Поскольку действие отправки в вашей форме перенаправляет на
show
действие по умолчанию, вы должны создатьshow.turbo_stream.erb
файл. - В указанный файл вы должны добавить два турбокадра, которые хотите обновить.
На мой взгляд, это избыточно для большинства приложений. Если вы хотите пойти по этому пути, вот полное обсуждение этой темы, включая полный пример от пользователя «kfk» внизу (подтверждено DHH).
Удачи! Надеюсь, это поможет!
Самый «горячий способ» замены HTML-содержимого на странице при действии пользователя - это использование ответа турбо-потока.
Если я правильно понял, вы хотите вернуть форму (или просто кнопку) в исходное состояние после отправки формы.
Вы бы обернули свою форму турбо-рамкой, вот так
<%= turbo_frame_tag dom_id(@invitation) do %>
<%= form_with(...) %>
<% end %>
<% end %>
Затем, как сказал @stwienert, пусть ваш контроллер вернет
format.turbo_stream
ответ, который отображает частичный шаблон / для вашей формы. Вот пример ответа turbo_stream, адаптированный из документации Turbo :
format.turbo_stream do
render turbo_stream: turbo_stream.replace(@invitation, partial: "your/form",
locals: { invitation: @invitation }) # or Invitation.new, perhaps
end
Цель будет заключаться в том, чтобы ответ вашего контроллера нацелился на идентификатор DOM вашей формы, так что при отправке формы ваши данные обрабатываются соответствующим образом, а затем клиент получает ответ турбо-потока, содержащий именно тот HTML-код, который вы хотите видеть внутри своего тега turbo_frame.
Если вы хотите заменить только кнопку, оберните только кнопку в turbo_frame, дайте ей уникальный идентификатор DOM и передайте этот идентификатор в turbo_stream.replace / append / etc, например
turbo_stream.replace(:button_dom_id_here, partial: ...)
Я бы, наверное, добавил в form.submit «исходный» текст:
<% = form.submit "Invite", data: {
disable_with: "...", original: "Invite", "data-reset-form-button-target" } %>
Тогда вы можете улучшить свой контроллер стимулов:
static targets = ["button"]
reset() {
this.element.reset()
this.buttonTarget.value = this.buttonTarget.dataset.original
}
Не смог проверить, просто набросал.
data-disable-with
is from , который на самом деле не предназначен для работы (хотя это может сбивать с толку из-за усилий по плавному переходу от UJS к ).
использует веб-сокеты, а использует AJAX. Подходы ортогональны.
Когда вы переключаетесь с на
Turbo
, вы теряете
disable-with
удобство, и вы вернете контент через
turbo_stream
против.
js
(
UJS
/AJAX).
Сброс формы очищает ввод, но не переключает
disabled
атрибут кнопки отправки.
Замена всей формы через Websockets даст вам новую форму.