Возможно, я что-то здесь упускаю, но похоже, что если вы используете Turbo и Action Cable в Rails 7, вы могли бы использовать состояние гонки, транслируя самому себе.
Например, предположим, что у меня есть список дел, и когда вы добавляете элемент, есть create.turbo_stream.erb
, который выполняет turbo_stream.append 'todoList', partial: ...
.
Поэтому, когда вы добавляете элемент, он добавляется к #todoList
, хорошо.
НО, допустим, у вас также есть Action Cable или трансляция Turbo::StreamsChannel, которая происходит after_create
, вот так:
Turbo::StreamsChannel.broadcast_append_to('todo-stream', {
target: 'todoList',
partial: 'todo',
locals: { todo: todo }
})
Итак, теперь происходит следующее: create.turbo_stream.html
добавляет элемент, а затем трансляция ТАКЖЕ добавляет элемент в список дел!
Я предполагал, что трансляция будет достаточно «умной», чтобы знать, что клиент, инициировавший трансляцию, не должен получать трансляцию, но, похоже, это не так.
Так как же мне с этим справиться? Я не могу просто заменить файл create.turbo_stream.html трансляцией, поскольку более сложные методы, такие как dropzone.js или sortable.js, приводят к тому, что последний элемент находится в определенном месте, поэтому трансляция замены не имеет смысла.
@max Я категорически не согласен. Ответ Алекса полностью соответствовал вопросу и помог мне его решить. Без этого другие могут столкнуться с той же проблемой.
Во-первых, убедитесь, что у вас есть уникальный идентификатор для вашего частичного списка задач. Это предотвратит добавление дублирующихся элементов и вместо этого заменит их:
# app/views/todos/_todo.html.erb
<%= tag.div id: dom_id(todo) do %>
# ...
<% end %>
Если первый элемент шаблона имеет идентификатор, который уже используется прямым дочерним элементом внутри контейнера, на который нацелен dom_id, он заменяется, а не добавляется.
https://turbo.hotwired.dev/reference/streams#append
Если замена дублированного id
каким-то образом вызывает проблему, единственное решение, которое я нашел, — это повторить то, что делает действие refresh
, отслеживая идентификатор запроса:
class Todo < ApplicationRecord
# this will broadcast a turbo stream with request id attribute:
# <turbo-stream request-id = "b3937918-30cb-47de-8bb0-020ac0d5c2dd" action = "append" target = "todos">
# ...
# </turbo-stream>
after_create_commit do
broadcast_append_to(:todos, attributes: {"request-id": Turbo.current_request_id})
end
end
# app/controllers/todos_controller.rb
def create
@todo = Todo.new(todo_params)
if @todo.save
render turbo_stream: turbo_stream.append(:todos, @todo)
end
end
Идентификатор запроса отправляется из внешнего интерфейса в виде заголовка X-Turbo-Request-Id
.
Когда придет этот турбопоток, вам нужно сравнить атрибут request-id
с недавним запросом:
// app/javascript/application.js
const originalAppend = Turbo.StreamActions.append
// override append action and do what the `refresh` action does:
Turbo.StreamActions.append = function() {
const session = Turbo.session
const isRecentRequest = this.requestId && session.recentRequests.has(this.requestId)
if (!isRecentRequest && !session.navigator.currentVisit) {
originalAppend.bind(this).call()
}
}
https://github.com/hotwired/turbo/blob/v8.0.5/src/core/streams/stream_actions.js#L52
https://github.com/hotwired/turbo/blob/v8.0.5/src/core/session.js#L110
ух ты. действительно чудесно, вау. Итак, это ПОЧТИ сработало, но не по вашей вине. Проблема в том, что я делал сообщение сервера с помощью FETCH, поскольку для обновления списка я использую sortable.js. Это вариант использования. Итак, используя приведенное выше в качестве руководства, я в конечном итоге заменил fetch
на Turbo.fetch,
, что позволило мне получить Turbo.current_request_id
и передать его обратно в пользовательское действие Stream, которое обрабатывает все остальное. Спасибо!
Я голосую за закрытие этого вопроса, поскольку он был решен способом, не связанным напрямую с исходным вопросом. Пожалуйста, рассмотрите возможность его удаления, поскольку подобные вопросы не очень полезны другим.