ターボストリームによるスムーズな移行

DDD
リリース: 2024-10-11 10:26:01
オリジナル
481 人が閲覧しました

This article was originally published on Rails Designer


With Turbo Streams you can update specific parts of your app. Inject a chat message, update a profile picture or insert a Report is being created alert.

The preciseness Turbo Streams offers is great. But often the abruptness of it, its not too appealing to me. The new component is just there (or isn't, if you remove it).

I'd like to add a bit more joy to my apps and this technique is something that does just that. I previously explored multiple techniques to add some kind of transition or animation when an element was inserted or removed. I fine-tuned it over the years while using it in production. And I can say I'm happy with how the technique works I am outlining today.

First, this will be the end result:

Smooth Transitions with Turbo Streams

And this is how it's used:

<%= turbo_stream_action_tag_with_block "prepend", target: "resources", data: {transition_enter: "transition ease-in duration-300", transition_enter_start: "opacity-0", transition_enter_end: "opacity-100"} do %>
  <%= render ResourceComponent.new(resource: @resource) %>
<% end %>
ログイン後にコピー
ログイン後にコピー

If you are using Rails Designer it is included for you and ready to go. Winning! ?

There are quite a few moving elements here to get it going, but as seen from the code example above, the usage is really clean and customizable.

Let's get started.

The first step is to create the turbo_stream_action_tag_with_block helper. This is needed, because the available turbo_stream_action_tag helper doesn't allow a block to be passed (eg. render CollectionComponent) and the the default Turbo stream Rails helpers don't pass along data-attributes. It's simple enough though:

# app/helpers/turbo_stream_helper.rb
module TurboStreamHelper
  def turbo_stream_action_tag_with_block(action, target: nil, targets: nil, **options, &block)
    template_content = block_given? ? capture(&block) : nil

    turbo_stream_action_tag(action, target: target, targets: targets, template: template_content, **options)
  end
end
ログイン後にコピー

Next up is to add a listener for turbo:before-stream-render and add some custom behavior.

// app/javascript/utilities/turbo_stream_render.js
document.addEventListener("turbo:before-stream-render", (event) => {
  const { target } = event

  if (!(target.firstElementChild instanceof HTMLTemplateElement)) return

  const { dataset, templateElement } = target
  const { transitionEnter, transitionLeave } = dataset

  if (transitionEnter !== undefined) {
    transitionEnter(event, templateElement, dataset)
  }

  if (transitionLeave !== undefined) {
    handleTransitionLeave(event, target, dataset)
  }
})

const handleTransitionEnter = (event, templateElement, dataset) => {
  event.preventDefault()

  const firstChild = templateElement.content.firstElementChild

  Object.assign(firstChild.dataset, dataset)

  firstChild.setAttribute("hidden", "")
  firstChild.setAttribute("data-controller", "appear")

  event.target.performAction()
}

const handleTransitionLeave = (event, target, dataset) => {
  const leaveElement = document.getElementById(target.target)
  if (!leaveElement) return

  event.preventDefault()

  Object.assign(leaveElement.dataset, dataset)

  leaveElement.setAttribute("data-controller", "disappear")
}
ログイン後にコピー

Wow! This looks scary! It's not too bad really! It intercepts the turbo:before-stream-render event, checking the target element's dataset for specific transition attributes (eg. data-transition-start). For entering elements, it sets them to hidden and adds an appear data-controller. For leaving elements, it adds a disappear data-controller.

? Want to be more comfortable writing and understanding JavaScript as a Ruby on Rails developer? Check out the book JavaScript for Rails Developers

Make sure to import it into your application.js.

 // app/javascript/application.js
 import "@hotwired/turbo-rails"
 import "./controllers"
 import "./utilities/turbo_stream_render.js"

 // …
ログイン後にコピー

Let's create those two controllers now. They are really simple and rely on the cool el-transition library. Make sure to add it to your app (either via NPM or importmaps).

// app/javascript/controllers/appear_controller.js
import ApplicationController from "./application_controller"
import { enter } from "el-transtion"

export default class extends ApplicationController {
  connect() {
    enter(this.element)
  }
}
ログイン後にコピー
// app/javascript/controllers/disappear_controller.js
import ApplicationController from "./application_controller"
import { leave } from "el-transtion"

export default class extends ApplicationController {
  connect() {
    leave(this.element).then(() => {
      this.element.remove()
    })
  }
}
ログイン後にコピー

And with that all out of the way, you can now add smooth transitions to any added or removed element using turbo streams.

Simply append some data-attributes (data: {transition_enter: ""}) to the turbo stream and enjoy a smooth ride.

You can use the same data-attributes supported by el-transition:

  • data-transition-enter;
  • data-transition-enter-start;
  • data-transition-enter-end;
  • data-transition-leave;
  • data-transition-leave-start;
  • data-transition-leave-end.

For adding elements:

<%= turbo_stream_action_tag_with_block "prepend", target: "resources", data: {transition_enter: "transition ease-in duration-300", transition_enter_start: "opacity-0", transition_enter_end: "opacity-100"} do %>
  <%= render ResourceComponent.new(resource: @resource) %>
<% end %>
ログイン後にコピー
ログイン後にコピー

And for removing elements:

<%= turbo_stream_action_tag_with_block "remove", target: dom_id(@resource), data: {transition_leave: "transition ease-in duration-300", transition_leave_start: "opacity-100", transition_leave_end: "opacity-0"} %>
ログイン後にコピー

以上がターボストリームによるスムーズな移行の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート