Skip to main content
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Rachel Makrucki
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Rachel Makrucki

Creating A ColdFusion CFSetting Tag With Open / Close Tags

By
Published in Comments (2)

In ColdFusion, the CFSetting tag is not designed to have an open and a close tag. It is merely used to define page processing rules for the rest of the page, NOT just for a portion of the rest of the page. I just jumped on a project where someone was trying to this:

<!--- Enable output only for this block. --->
<cfsetting enablecfoutputonly="true">

	<p>
		Lost of code here
	</p>

</cfsetting>

This was on an included page. I was trying to modify the parent page and was going nuts trying to figure out why my only SOME of my code was not displaying after the include. When I went into the include, it became obvious, and very irritating. Each CFSetting tag with EnableCFOutputOnly set must have a matching CFSetting tag that changes that setting back (if you want to change it back):

<!--- Enable output only. --->
<cfsetting enablecfoutputonly="true" />

<p>
	Lost of code here
</p>

<!--- Disable output only. --->
<cfsetting enablecfoutputonly="false" />

I, however, do like the idea of creating a CFSetting tag that DOES work on a block of code. So, I set about creating one myself. The first obstacle was determining if CFSetting EnableCFOutputOnly was turned on or off at the start of the tag. I starting digging through the GetPageContext() object. I figured it had to be a variable set somewhere. After about an hour of searching it dawned on me!! I don't need to find a variable for it - I just need to test it.

To test if output is required, just try to write some text and see if it actually get's written:

<!--- Test for EnableCFOutputOnly. --->
<cfsavecontent variable="strOutputTest">test</cfsavecontent>

<!---
	Check to see if the tested string has any length. If it does,
	then CFOutput is not required. If it does not, then CFOutput
	was in fact, required.
--->
<cfif strOutput.Length()>

	<!--- CFOutput NOT required. --->

<cfelse>

	<!--- CFOutput required. --->

</cfif>

Once I figured this out, the custom tag itself is not TOO complicated. All you have to do is check the current status of EnableCFOutputOnly, store it in the tag data, override it, then, in the close tag, restore it to what it was. The complication is that if you have nested EnableCFOutputOnly settings, then to output in a sub-block, you have to Undo the setting several times, which then of course requires resetting it several times at the end:

<!--- Kill extra output. --->
<cfsilent>

	<!---
		Check to see which mode of the tag we are in. If we are
		in the start mode of the tag then we want to param the
		attributes and check to see if cfoutput is required for
		data output.
	--->
	<cfif NOT CompareNoCase( THISTAG.ExecutionMode, "Start" )>

		<!---
			Param the tag attributes. These are the attributes
			that are normally allowed for the CFSetting tag.
		--->

		<cfparam
			name="ATTRIBUTES.requesttimeout"
			type="string"
			default=""
			/>

		<cfparam
			name="ATTRIBUTES.showdebugoutput"
			type="string"
			default=""
			/>

		<cfparam
			name="ATTRIBUTES.enablecfoutputonly"
			type="string"
			default=""
			/>


		<!--
			Check to see if CFOutput is required. We can
			check this by storing a string and then testing
			it for a length.
		--->
		<cfsavecontent
			variable="strCFOutputTest"
			>test</cfsavecontent>

		<!--- Check to see if the test resulted in a length. --->
		<cfif Len( strCFOutputTest )>

			<!--- CFOutput is not required. --->
			<cfset THISTAG.CFOutputRequired = false />

		<cfelse>

			<!--- CFOutput IS currently required. --->
			<cfset THISTAG.CFOutputRequired = true />

			<!---
				When cfoutput is required, things get a bit
				more tricky. Enabling it once might not
				show any output. We need to keep disabling
				it until output is available.

				Check to see if we are going to disabling
				CFOutput for this code block.
			--->
			<cfif (
				IsBoolean( ATTRIBUTES.enablecfoutputonly ) AND
				(NOT ATTRIBUTES.enablecfoutputonly)
				)>

				<!---
					Since we are going to disable the cfoutput
					requirements, we need to keep doing so
					until output is allowed. Set a counter for
					the number of nested tags.
				--->
				<cfset THISTAG.CFSettingCounter = 0 />

				<!---
					Keep looping until the test content has a
					length.
				--->
				<cfloop condition="NOT Len( strCFOutputTest )">

					<!--- Disable output requirements. --->
					<cfsetting enablecfoutputonly="false" />

					<!--- Try getting output again. --->
					<cfsavecontent
						variable="strCFOutputTest"
						>test</cfsavecontent>

					<!--- Add one to counter. --->
					<cfset THISTAG.CFSettingCounter = (
						THISTAG.CFSettingCounter + 1
						) />

				</cfloop>

			</cfif>

		</cfif>


		<!--- Check to see which page values we need to set. --->
		<cfif Len( ATTRIBUTES.requesttimeout )>

			<cfsetting
				requesttimeout="#ATTRIBUTES.requesttimeout#"
				/>

		</cfif>

		<cfif Len( ATTRIBUTES.showdebugoutput )>

			<cfsetting
				showdebugoutput="#ATTRIBUTES.showdebugoutput#"
				/>

		</cfif>

		<cfif Len( ATTRIBUTES.enablecfoutputonly )>

			<cfsetting
				enablecfoutputonly="#ATTRIBUTES.enablecfoutputonly#"
				/>

		</cfif>


	<!---
		We only want to set it back if the tag had a REAL
		closing end tag. If it had a self closing tag,
		then there will be no generated content.

		YES, it maybe be possible to be using this and NOT
		actually create any generated content. But in that
		case, the user should be using a CFSilent tag, NOT a
		CFSetting tag.

		Also, we ONLY care about the end tag if we set the
		EnableCFOutputOnly attribute. None of the other
		attributes make sense having an end tag.
	--->
	<cfelseif (
		THISTAG.GeneratedContent.Length() AND
		IsBoolean( ATTRIBUTES.EnableCFOutputOnly )
		)>

		<!---
			This is the end mode of the tag. Now, we need to
			restore the enablecfoutput mode that was set when
			this tag was first executed. Check to see which
			kind of change we are making, or rather, what did
			we have beforehand.
		--->
		<cfif THISTAG.CFOutputRequired>

			<!---
				CFOutput was required before. If we turned it
				on this time, we have to turn it off this
				time instead of just resetting it. If we
				attempt to just reset it, then we will
				merely be adding another nesting of CFSetting
				which is not what we want to do.
			--->
			<cfif ATTRIBUTES.EnableCFOutputOnly>

				<!---
					Counteract this one by just turning it
					off. This will put us back to the parent
					setting.
				--->
				<cfsetting
					enablecfoutputonly="false"
					/>

			<cfelse>

				<!---
					We were briefly turning it off. Therefore,
					we may need to turn it back on several times
					to mimic the original nestinf of the
					CFSetting tags.
				--->
				<cfloop
					index="intI"
					from="1"
					to="#THISTAG.CFSettingCounter#"
					step="1">

					<cfsetting
						enablecfoutputonly="true"
						/>

				</cfloop>

			</cfif>

		<cfelseif ATTRIBUTES.EnableCFOutputOnly>

			<!---
				Output was NOT required previously but we set
				it for the duration of this tag. Now, just
				turn it off.

				We dont care if we turned it off. You cannot
				nest "false" output enabling, only "true" ones.
			--->
			<cfsetting
				enablecfoutputonly="false"
				/>

		</cfif>

	</cfif>

</cfsilent>

Now, you can use this tag to do the following:

<cfsetting enablecfoutputonly="true" />

<p>
	Text area [ A ]
</p>

<cf_cfsetting enablecfoutputonly="true">

	<p>
		Text area [ B ]
	</p>

	<cfoutput>
		<p>
			Text area [ C ]
		</p>
	</cfoutput>

	<cf_cfsetting enablecfoutputonly="false">

		<p>
			Sub area [ 1 ]
		</p>

		<cfoutput>
			<p>
				Sub area [ 2 ]
			</p>
		</cfoutput>

	</cf_cfsetting>

	<p>
		Sub area [ D ]
	</p>

</cf_cfsetting>

<p>
	Text area [ E ]
</p>

<cfsetting enablecfoutputonly="false" />

<p>
	Text area [ F ]
</p>

<cfsetting enablecfoutputonly="false" />

<p>
	Text area [ G ]
</p>

<cfsetting enablecfoutputonly="false" />

<p>
	Text area [ H ]
</p>

<cfoutput>
	<p>
		Text area [ I ]
	</p>
</cfoutput>

If the above code used a standard CFSetting tag, then you would get:

Text area [ C ]
Sub area [ 2 ]
Text area [ F ]
Text area [ G ]
Text area [ H ]
Text area [ I ]

Text area [ E ] would not show since the EnableCFOutputOnly attribute would NOT be closed by the close CFSetting tag. And, the Sub area [ 1 ] would not show since the enablecfoutputonly="false" of the second nested tag would NOT override both previous CFsetting attributes. However, since this custom tag restores the pre-tag CFOutput requirements, you end up getting:

Text area [ C ]
Sub area [ 1 ]
Sub area [ 2 ]
Text area [ F ]
Text area [ G ]
Text area [ H ]
Text area [ I ]

Notice that Sub area [ 1 ] is getting displayed. That is because the enablecfoutputonly="false" overrides BOTH the previous CFSetting attributes. And yet, at the same time, Text area [ D ] does NOT show since after the close cf_cfsetting tag, the enablecfoutputonly="true" of the previous tag is restored. Notice that if you took out the nested tags, you would only get areas F, G, H, and I, which is according to the rules of CFSetting.

So basically, this cf_CFSetting tag allows you to determine the requirements of CFOutput regardless of the nested nature of the CFSetting tags before and after the current tag.

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

Reader Comments

50 Comments

There's some slick logic behind this tag. Great work, man.

I'd like to share something I just found out today. CFsetting with enablecfoutputonly set to "true" is meaningless for anything within a CFform.

Try this out.

<cfsetting enablecfoutputonly="true">

No cfoutput<br />

<cfoutput>
Yes cfoutput<br />
</cfoutput>

<cfform action="#CGI.script_name#">

CFFORM No cfoutput<br />

<cfoutput>
CFFORM Yes cfoutput<br
</cfoutput>

</cfform>

I figured this out when I had a custom tag inside a CFform, and I was getting all this white space that was supposed to be suppressed. The custom tag was using CFsetting to silent out everything except for whatever was inside CFoutput tags. It was only through trial and error that I was able to figure this bug out.

I ended up going back to CFprocessingdirective with suppresswhitespace set to "true" to solve the problem. Nonetheless, I do consider this a fail for CFform.

What's really bizarre is that whatever is inside a CFform does NOT get treated as if it were within CFoutput tags. For example, this code does not actually output the contents of the variable.

<cfform action="#CGI.script_name#>
#CGI.script_name#
</cfform>

This is really strange behavior. I imagine something like this is so small that no one will really care. Plus, CFform just has such a bad rap as it is.

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