Closed danecando closed 6 months ago
@danecando what would be an example use case for this? In my usage so far, you either use Turbo Stream -or- you morph. I can’t imagine using a stream to trigger a morph.
When I’m doing a turbo stream, it’s because I’m updating the specific piece of the page that I care about. Are you using Turbo Stream for a large section?
I think the use cases are probably less than I had suspected initially. Probably replacing larger chunks of content or things containing images to prevent additional requests or potential flashes
I had a use case when I wanted to animate a list of item using https://auto-animate.formkit.com/ When an action creates a new item I could not just append it to the list as the list was sorted. So I decided to render the full list back as a stream. A standard update would not play the animation. Using a morph stream only the new item got appended to the list at the correct location and with the animation. They are probably other ways to do this with lifecycle event but here that made the solution pretty straight forward.
@adrienpoly Are you suggesting that there is already a turbo stream action for triggering a morph? Or do you mean that you chose to not use a turbo stream action and instead triggered a page refresh / morph of the whole page?
Overall, I don’t think it’s right to conceive of morph as desired turbo stream action. I’d suggest we probably close this as an issue.
Page Refresh w/ morph and Turbo Stream actions are really alternatives to each other. We are intended to pick one or the other.
If you want to be surgical about your updates to the page, you wire up a bunch of Turbo Stream actions which makes just the right update/append/insert as needed. Or if that’s too much work, you can instead trigger a full Page Refresh and tell it to morph what it gets back. It’s a little heavier handed (e.g. two HTTP responses and a larger payload) but not too much so and it should just magically work.
@krschacht no there is no official Morph but adding a custom action is fairly easy now. https://marcoroth.dev/posts/guide-to-custom-turbo-stream-actions. They are even a few packages to get it out of the box
I was just giving a use case where Stream morph has been useful for me in the past. Maybe today I would build it with a full page morph 🤷♂️
@adrienpoly Ah, yes, that’s not crazy. I think this is a great example of why there i support for custom actions.
I’d say: custom actions is the right “solution” the library provides for this need and adding it as an official turbo stream action wouldn’t be right. This is a case where the library making this “difficult” is a feature rather than a bug.:)
There are definitely reasons why you'd want to have an independent morph action. Morphing has the benefit of not loosing client-side state. In any case where you are currently using turbo_stream.replace
or turbo_stream.update
you could consider upgrading that call to a turbo_stream.morph
. I've personally done this quite often.
But even if we don't add a morph
action into the official package, there is turbo_power
or the stripped down version turbo-morph
that provide a morph
action out of the box.
@krschacht no there is no official Morph but adding a custom action is fairly easy now. https://marcoroth.dev/posts/guide-to-custom-turbo-stream-actions. They are even a few packages to get it out of the box
Downside being the user would have to load the Idiomorph dependency twice since it's not exposed in the Turbo bundle - unless I'm mistaken?
I'm working on a project where I'd to create a morph
action to update the content of a modal. It works as follow. First, the user navigates to a page containing a list of foods, then there's an "Add Food" link that returns an append
action adding the modal element to the page. Inside this modal, the user can search and choose one or more foods to add to the list. Each time the search form is submitted, it returns a morph
action updating only the content of the modal. The modal is already wrapped in a Turbo Frame but I still opted for morphing to preserve the already selected foods and avoid the focus jumping to the search input, because of autofocus
, whenever the user makes another search. The selected foods are stored as input[type=hidden]
elements that I programmatically add or remove from the DOM. Before that, I checked https://github.com/marcoroth/turbo_power but it uses morphdom and I didn't want to keep two different morphing libraries.
Downside being the user would have to load the Idiomorph dependency twice since it's not exposed in the Turbo bundle - unless I'm mistaken?
I think idiomorph should be a peer-dependency or exposed so we can reuse it elsewhere.
FWIW, we have a use case similar to @m4theushw and would make heavy use of turbo stream morphs. We use a lot of modals, especially on expensive pages, where the modal will load a lightweight form. When making selections in the form, we often want to re-render it, since certain selected fields might change other parts of the form. We don't want to reload the whole page, because the modal is sitting on top of an expensive page, and when a form changes, you want it to re-render as quickly as possible. innerHTML
is also a bad use case for re-rendering a form more generally, because the focused input will change, which is disorienting for the user.
Stimulus Reflex has a similar distinction, where you can do either a full page morph or a selector morph. But with that library, you can use either morphdom
or innerHTML
for both of those use cases. That makes more sense to me. Sometimes I want to completely blow away the previous DOM and replace it with a new tree. Other times I want to merge in changes but keep the client-side state, event listeners, and element focus. But I don't think about the difference in those two use cases being the difference in replacing the whole page vs part of a page.
I recently spiked out adding morphdom
as a custom action (e.g. turbo_stream.morph
), and it works fine. But just wanted to add another voice saying that would be a useful first-class feature.
I added a PR to address this. For me the main motivation is being able to morph a component/element on the page while preserving the state. That can be handy in so many use cases.
@seanpdoyle I think this issue can be closed 🎉
@afcapel or @jorgemanrubia could either of you close this issue?
Would be nice to support a
morph
turbo stream action for users who maybe aren't ready to opt in or transition to full page morphing.