Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 1, 2023 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/73b378209bc440660eca2802fbae182f to your computer and use it in GitHub Desktop.
Save bennadel/73b378209bc440660eca2802fbae182f to your computer and use it in GitHub Desktop.
Transcluding A Form Into A Turbo Frame Using Hotwire And Lucee CFML
component
output = false
hint = "I define the application settings and event handlers."
{
// ... truncated code ... //
/**
* I get called once to initialize the request.
*/
public void function onRequestStart() {
request.context = structNew()
.append( url )
.append( form )
;
request.isGet = ( cgi.request_method == "get" );
request.isPost = ! request.isGet;
request.template = {
statusCode: 200,
statusText: "OK"
};
// When Turbo Drive makes requests to the ColdFusion server, it will modify the
// HTTP request headers in two ways:
//
// 1. If the request is made in the context of a Turbo Frame, the ID of the frame
// will be included as the header, "Turbo-Frame".
//
// 2. If the request is a FORM submission, which can accept a Turbo Stream style
// response, a new type will be appended to the "Accept" header.
var headers = getHttpRequestData( false ).headers;
var turboFrame = ( headers[ "Turbo-Frame" ] ?: "" );
request.turbo = {
isFrame: turboFrame.len(),
frame: turboFrame,
isStream: ( headers[ "Accept" ] ?: "" ).findNoCase( "text/vnd.turbo-stream.html" )
};
}
}
<cfscript>
param name="form.text" type="string" default="";
errorMessage = "";
if ( request.isPost ) {
try {
application.noteService.createNote( form.text.trim() );
// If we are in Turbo Frame and can support a Turbo Stream response, then we
// will execute the REDIRECT using our custom Turbo Stream "visit" action.
if ( request.turbo.isFrame && request.turbo.isStream ) {
include "./create_stream.cfm";
exit;
// If this is a normal top-level page action, then let's redirect back to the
// main page as per usual.
} else {
location( url = "index.htm", addToken = false );
}
} catch ( any error ) {
errorResponse = application.errorService.getResponse( error );
request.template.statusCode = errorResponse.statusCode;
request.template.statusText = errorResponse.statusText;
errorMessage = errorResponse.message;
}
}
</cfscript>
<cfmodule template="./tags/page.cfm">
<cfoutput>
<h2>
Add Note
</h2>
<!---
In order for this note creation form to be transcluded (in to the main page)
by Turbo Drive, we have to wrap it in a Turbo Frame that corresponds to the
lazy-loaded frame in the main page. For that, we'll use the frame ID that was
reported in the HTTP headers (to reduce duplication).
--
NOTE: We are using target="_top" in this context so that if this page were
loaded directly (via the URL), it would operate as if it weren't in a frame.
--->
<turbo-frame
id="#encodeForHtmlAttribute( request.turbo.frame )#"
target="_top">
<cfif errorMessage.len()>
<p class="error-message">
#encodeForHtml( errorMessage )#
</p>
</cfif>
<form method="post" action="create.htm">
<p>
<input type="text" name="text" size="40" autofocus />
</p>
<p>
<button type="submit">
Add Note
</button>
<!---
This cancel button will be hidden (via the CSS class) when it is
transcluded into the main page.
--->
<a href="index.htm" class="hide-if-transcluded">
Cancel
</a>
<!---
If this form is being transcluded into the main page, and it has
an error message, let's show a button to clear / reset the form
(to remove the error message). Depending on the type of form you
have, this may not be necessary.
--->
<cfif ( request.turbo.isFrame && errorMessage.len() )>
<a href="create.htm" data-turbo-frame="_self">
Reset
</a>
</cfif>
</p>
</form>
</turbo-frame>
</cfoutput>
</cfmodule>
<cfcontent type="text/vnd.turbo-stream.html; charset=utf-8" />
<cfoutput>
<!--- Refresh the create form Turbo Frame. --->
<turbo-stream
action="visit"
data-url="create.htm"
data-frame="create-frame">
</turbo-stream>
<!--- Refresh the list of notes Turbo Frame. --->
<turbo-stream
action="visit"
data-url="index.htm"
data-frame="notes-list">
</turbo-stream>
</cfoutput>
<cfscript>
notes = application.noteService.getNotes().reverse();
</cfscript>
<cfmodule template="./tags/page.cfm">
<cfoutput>
<h2>
Notes
</h2>
<!---
This Turbo Frame is LAZY LOADED and will inline the create-note form. The
static contents of the form will be rendered as a fallback while the remote
template is being loaded.
--
NOTE: The "transcluded" CSS class is included to help us hide elements of the
form after it has been loaded and inlined by Turbo Drive.
--->
<turbo-frame
id="create-frame"
src="create.htm"
data-turbo-permanent
class="transcluded">
<p>
<a href="create.htm">Add Note</a>
</p>
</turbo-frame>
<!---
This Turbo Frame gives us a target to RELOAD after a new note has been added.
The RELOAD functionality will be implemented as a custom Turbo Stream action.
--->
<turbo-frame id="notes-list">
<ul>
<cfloop item="note" array="#notes#">
<li>
#encodeForHtml( note.text )#
&mdash;
<a
href="delete.htm?id=#encodeForUrl( note.id )#"
data-turbo-method="delete"
data-turbo-confirm="Delete this note?">
Delete
</a>
</li>
</cfloop>
</ul>
<cfif notes.len()>
<p>
<a
href="clear.htm"
data-turbo-method="delete"
data-turbo-confirm="Delete all notes?">
Clear all notes
</a>
</p>
</cfif>
</turbo-frame>
</cfoutput>
</cfmodule>
// Import core modules.
import * as Turbo from "@hotwired/turbo";
import { StreamActions } from "@hotwired/turbo";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
/**
* I support Turbo.visit() stream actions.
*/
StreamActions.visit = function() {
var url = this.dataset.url;
var action = ( this.dataset.action || "advance" );
var frame = ( this.dataset.frame || undefined );
Turbo.visit(
url,
{
action: action,
frame: frame
}
);
}
.transcluded {
.hide-if-transcluded {
display: none ;
}
}
<turbo-frame
id="create-frame"
src="create.htm"
data-turbo-permanent
class="transcluded">
<!--- Static content --->
</turbo-frame>
<a href="index.htm" class="hide-if-transcluded">
Cancel
</a>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment