Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Using FrameworkOne (FW/1) Layouts To Strip Whitespace In Lucee CFML 5.3.7.47

By Ben Nadel on
Tags: ColdFusion

At InVision, we use FrameworkOne (FW/1) as our ColdFusion / CFML web application framework. With FW/1, you can define a Controller, a collection of Views, and a Layout for a given feature-set. The Views get rendered and then "rolled up" into the Layout (optionally) at which point they are served to the client. Yesterday, I came up with a fun use-case for Layouts - I had to generate a View that had a lot of data on it (it was a report). So, in an effort to minimize the number of bytes that I was sending over the network, and to minimize the client-side DOM (Document Object Model) structure, I used the FW/1 Layout to strip out whitespace from the response. I had never used FW/1 in this way before; so, I thought it might make for an interesting demo in Lucee CFML 5.3.7.47.

With FW/1, each "subsystem" - which is a macro-organizational unit within the framework - has a directory structure that looks like this:

  • Subsystem/
    • controllers/
      • my_feature.cfc
    • layouts/
      • my_feature.cfm
    • views/
      • my_feature/
        • default.cfm

FW/1 is a convention-based framework, which means that the names of the various files and directories are used to auto-wire the response composition. In this case, the my_feature.cfc is the Controller whose default method is defult(). This method is automatically wired into the default.cfm View, which is in turn, automatically rolled-up into the my_feature.cfm Layout.

When a View is being merged into a Layout, it is exposed to the Layout template as the body variable. This allows the Layout to provide the overall HTML structure and then just interpolate the View(s) using #body#:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>
		A Simple FW/1 Layout Template
	</title>
</head>
<body>

	<!--- Embed the current View output into the Layout. --->
	<cfoutput> #body# </cfoutput>

</body>
</html>

Yesterday, in my case, the report that I was generating didn't really have a Layout. Or, rather, the layout HTML was embedded directly within the View and I didn't need to have an additional Layout template to wrap the View. With FW/1 you can tell the request processing to skip the Layout by setting request.layout to false:

<!--- DO NOT WRAP THIS VIEW IN A LAYOUT. --->
<cfset request.layout = false />

<cfoutput>

	<!doctype html>
	<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>
			A FW/1 View That Does Not Need A Layout
		</title>
	</head>
	<body>

		<!--- Lots of REPORT GENERATION LOGIC. --->
		<!--- Lots of REPORT GENERATION LOGIC. --->
		<!--- Lots of REPORT GENERATION LOGIC. --->
		<!--- Lots of REPORT GENERATION LOGIC. --->
		<!--- Lots of REPORT GENERATION LOGIC. --->
		
	</body>
	</html>

</cfoutput>

The reporting View that I was generating was several megabytes in size. As such, I figured that any whitespace that I could remove as part of the request processing might be a win. Yes, GZIP should handle some compression; but, with the size of the View being so large, I figured that every little improvement might help the transfer and subsequent rendering time.

So, instead of having the View be "self contained", I removed the request.layout flag and, instead, wired it into a Layout that looked like this:

<!--- This is a stand-alone layout (even layouts can be nested in FW/1). --->
<cfset request.layout = false />

<!--- Reset the output buffer. --->
<cfcontent type="text/html; charset=utf-8" />
<!---
	Since this layout is going to produce a LARGE HTML payload, let's strip out	as much
	leading whitespace as possible. This should reduce both the transfer time over the
	wire and the size of the subsequent DOM structure that is parsed by the browser.
--->
<cfoutput>
	<!---
		Using a multi-line Regular Expression to strip any "space characters" from the
		beginning of each line.
	---> 
	#body.reReplace( "(?m)^\s+", "", "all" )#
</cfoutput>

Remember, within a FW/1 Layout, the body variable contains the rendered View markup. And, in this case, we're not just embedding the View within the Layout - we're modifying the View as part of the interpolation. Specifically, we're using Regular Expressions (RegEx), in multi-line pattern matching mode, to remove whitespace characters from the front of every newline in the View markup before we nested it within the Layout markup.

Now, if I render a FW/1 View with the given Layout, I get the following HTML response:

The HTML markup of a rendered page in FW/1 that has all leading whitespace removed in Lucee CFML.

As you can see, all leading whitespace characters have been stripped from every line in the document. You gotta love Regular Expressions, baby!

I am sure there are other ways to accomplish this with FrameworkOne (FW/1). To be honest, I know only enough about the framework to get my work done; I am - by no stretch of the imagination - an expert on FW/1 application development. That said, I thought this a fun little "hack".



Reader Comments

That's so cool! I use CFWheels and this concept would totally work with it too. Why not also remove the new line characters, joining all the lines? Love how you think outside the box! We'll done!

Reply to this Comment

@Chris,

Thanks! always get a bit nervous about removing the newline characters because I think I might accidentally join two strings together than need a "space" between them. So I figure as long as I need one white-space character to separate tokens, I might as well make it the "newline" so that the view-source is still somewhat readable.

I tried looking through the FW/1 docs to see if there was some sort of "post view" hook, where I might be able to do this more globally based on a rc variable. Imagine something like being able to set:

rc.removeViewIndentation = true;

... somewhere and then it would just magically work for that request.

Though, now that I say that, maybe there is some way that I can keep nesting the layout so that there is always a "remove whitespace layout" that I can wrap any other layout into based on some variable.

I know enough FW/1 to "get the job done". But, I'm not really familiar with all the ins-and-outs of the features.

Reply to this Comment

In CFWheels, we can set('removeViewIndentation') and get('removeViewIndentation) any variable we want to be accessed globally. Perhaps FW/1 has something similar. They also roll their templates up, one being included into the next until it reached the top level layout. I'd imagine your top level layout could get('removeViewIndentation') and remove the white space (or not) depending.

Reply to this Comment

@Chris,

Yeah, in FW/1 you can just keep jamming stuff in the rc scope (I think it stands for "Request Context" or "Request Collection"). The rc scope is available to all the controllers, views, and layouts. I tried poking around in the docs, and it does look like you can set a site-wide layout, which - to your point - you can probably just read from the rc scope there and apply some special logic.

It's gonna be a nice little trick to have in my back pocket!

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.