Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Dee Sadler
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Dee Sadler ( @DeeSadler )

Including Inline Turbo-Stream Actions In Hotwire And Lucee CFML

By on

In my first exploration of Turbo Streams in Hotwire, I was generating a dedicated response of type, text/vnd.turbo-stream.html, which was being returned within a Form POST response. Based on the Turbo documentation, I had thought that this was the only way in which to use Turbo Streams. But, I recently came across a post on the Hotwire developer forum where people were talking about including Turbo Stream elements inline with any kind of response (top-level page or Turbo Frame). This is a very interesting proposition that I wanted to explore in Lucee CFML.

View this code in my ColdFusion + Hotwire Demos project on GitHub.

As I discussed in my previous post, Turbo Stream elements define actions for Turbo Drive to perform in response to navigation events. The default actions are all DOM (Document Object Model) transformations, like append, prepend, replace, and update. But, Turbo allows you to define custom actions which might include features such as logging and redirection.

ASIDE: I haven't explored it yet, but the Ruby on Rails community seems to be favoring TurboPower, a 3rd-party set of Turbo Stream actions that includes interaction with cookies, styles, history, frames, etc.

To explore the definition and consumption of inline <turbo-stream> elements, I've created a very simple ColdFusion application. It has a main page and an asynchronously-loaded <turbo-frame>. Both the main page and the async frame will provide responses that have embedded <turbo-stream> elements.

Here's my main ColdFusion page - it includes a Div with id="messages". This Div will be mutated / transformed by the various inline <turbo-stream> directives.

<cfmodule template="./tags/page.cfm">
	<cfoutput>

		<h2>
			Welcome to My Site
		</h2>

		<div id="messages">
			This content will be replaced by the TURBO-STREAM actions below.
		</div>

		<turbo-frame id="async-frame" src="frame.htm">
			<!--- Will be loaded asynchronously. --->
		</turbo-frame>

		<!--- ---------------------------------------------------------------------- --->
		<!--- ---------------------------------------------------------------------- --->

		<!---
			Notice that the following Turbo-Stream elements are NOT part of a traditional
			"text/vnd.turbo-stream.html" response - they are being defined right inline
			with the page content. On load, Turbo Drive will locate them, apply the DOM
			(Document Object Model) transformations, and then remove them.
		--->

		<turbo-stream action="update" target="messages">
			<template>
				<p class="inline-action">
					This is the first message.
					I have <strong>reset</strong> the messages container.
				</p>
			</template>
		</turbo-stream>

		<turbo-stream action="append" target="messages">
			<template>
				<p class="inline-action">
					This is the second message.
				</p>
			</template>
		</turbo-stream>

	</cfoutput>
</cfmodule>

As you can see, there are two inline Turbo Stream elements: one that resets (update) the messages content with a first message; and, one that appends a second message. On top of that, we also have our asynchronously loaded Turbo Frame:

<turbo-frame id="async-frame">

	<p>
		This is the async FRAME content.
	</p>

	<!---
		In order for these inline Turbo-Stream elements to be picked up and applied to the
		live document, they have to be included inside of the Turbo-Frame element.
		Otherwise, they will not be transcluded into the calling page and will, therefore,
		be de facto discarded.
	--->

	<turbo-stream action="append" target="messages">
		<template>
			<p class="inline-action inline-action--async">
				This was injected via the async <strong>Turbo-Frame</strong> response.
			</p>
		</template>
	</turbo-stream>

</turbo-frame>

Again, this Turbo Frame includes an inline <turbo-stream> element representing a transformation of the messages container. Note that Turbo Frames are rendered based on a corresponding id attribute. As such, everything outside of the Turbo Frame will be discarded. This means that our Turbo Stream directives must be inside the rendered frame content.

Now, if we run our main ColdFusion page, we get the following output:

All three turbo-stream mutations have been applied to the DOM, showing three messages inside the messages container.

As you can see, all of the message transformations defined by our inline <turbo-stream> elements have all been applied to the DOM!

This is a really slick feature of Hotwire - I'm surprised that it isn't made more obvious in the documentation. I can see this being quite helpful in updating transient parts of the DOM in response to actions taken by the user.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel