Copy link to clipboard
Copied
When I try to conditionally close a cfmenu with a cfif tag I get the following error...
The tag <cfif>, on line 12, column 4, requires an end tag.
The <cfif> tag requires an end tag to nest within <cfmenu>, which began on line 10, column 4.The CFML compiler was processing:
* The body of a cffunction tag beginning on line 7, column 2.
* The body of a cffunction tag beginning on line 7, column 2.
* The body of a cffunction tag beginning on line 7, column 2.
The error occurred in C:\ColdFusion8\wwwroot\HMC\cfc\testnav.cfc: line 12
Called from C:\ColdFusion8\wwwroot\HMC\Nav.cfm: line 18
Called from C:\ColdFusion8\wwwroot\HMC\index.cfm: line 1010 : <cfmenu bgcolor="##443266" type="horizontal" selecteditemcolor="##7C768B">
11 : <cfset linksRead += 1>
12 : <cfif linksRead GTE amountOfLinks>
13 : </cfmenu>
14 : </cfif>
As you can see the </cfif> is clearly there.
Copy link to clipboard
Copied
I think your error message kind of answers your question, eh? But to confirm: no, one cannot do that.
A CFML code needs to be syntactically complete at compile time, so one cannot control the code composition at runtime. You have to bear in mind that the CF application server is not actually executing the CFML code in your files as you see it, the code is compiled first, then it is run. So there cannot be runtime decisions being made as to how the code is written, as the code needs to be written and compiled before it is run.
Make sense?
--
Adam
Copy link to clipboard
Copied
Yes, but I was hoping maybe I made a mistake elsewhere that would cause such an error. I am just frustrated that I can't seem to recursively populate a cfmenu.
Copy link to clipboard
Copied
I think you should be approaching that problem from the perspective of already knowing if you're going to need the submenu before you get to the <cfmenu> tag, rather than how you seem to be doing it (which I don't follow the logic of, admittedly).
I'll try to mock up some sample code, because code is probably easier to follow that some contorted narrative which I cannot work out how to articulate 😉
--
Adam
Copy link to clipboard
Copied
Thank you. Currently I just use nested loops; one loop for ever layer down. It works, but I don't see it as "good code" and it is not very flexible.
Copy link to clipboard
Copied
OK, that was slightly more of a challenge that I expected, because one cannot actually use recursion to output <cfmenuitem> tags, as they need to be within <cfmenu> tags. So generating <cfmenuitem> tags in a function call is not valid.
I have worked around this using the notion that menu data will not change very often, so it's perhaps viable to write the rendered code to file, and include the file. Some event handler can then be used to regenerate the file if the underlying data changes.
Here's a working proof of concept (attached).
I'm sure I can contrive a way of generating a fully "recursive" & fully dynanic menu using a single loop and pushing and popping the query and a query-pointer to/from a stack, but I can't be bothered thinking about it now.
--
Adam
Copy link to clipboard
Copied
Here's a working proof of concept (attached).
Well it would be attached if these forums didn't suck so much. Trying again.
Copy link to clipboard
Copied
Groan.
OK, here it is inline:
<cfset sMenuFile = "inc_menus.cfm">
<cfset sMenuFilePath= expandPath("./") & sMenuFile>
<cfif not fileExists(sMenuFilePath)>
<cfscript>
// these are the first few menu options from ColdFusion Builder
qMenuData = queryNew("ID,parentId,label","Integer,Integer,VarChar");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 1); querySetCell(qMenuData, "parentId", 0); querySetCell(qMenuData, "label", "File");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 2); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "New");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 3); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "ColdFusion Project");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 4); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "Project...");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 5); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "ColdFusion Page");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 6); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "ColdFusion Component");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 7); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "ColdFusion Interface");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 8); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "File");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 9); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "Folder");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 10); querySetCell(qMenuData, "parentId", 2); querySetCell(qMenuData, "label", "Other...");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 11); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Open File");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 12); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Close");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 13); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Close All");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 14); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Save");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 15); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Save As...");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 16); querySetCell(qMenuData, "parentId", 1); querySetCell(qMenuData, "label", "Save All");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 17); querySetCell(qMenuData, "parentId", 0); querySetCell(qMenuData, "label", "Edit");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 18); querySetCell(qMenuData, "parentId", 17); querySetCell(qMenuData, "label", "Undo");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 19); querySetCell(qMenuData, "parentId", 17); querySetCell(qMenuData, "label", "Redo");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 20); querySetCell(qMenuData, "parentId", 17); querySetCell(qMenuData, "label", "Cut");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 21); querySetCell(qMenuData, "parentId", 17); querySetCell(qMenuData, "label", "Copy");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 22); querySetCell(qMenuData, "parentId", 17); querySetCell(qMenuData, "label", "Paste");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 23); querySetCell(qMenuData, "parentId", 0); querySetCell(qMenuData, "label", "Navigate");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 24); querySetCell(qMenuData, "parentId", 23); querySetCell(qMenuData, "label", "Go Into");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 25); querySetCell(qMenuData, "parentId", 23); querySetCell(qMenuData, "label", "Go To");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 26); querySetCell(qMenuData, "parentId", 25); querySetCell(qMenuData, "label", "Back");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 27); querySetCell(qMenuData, "parentId", 25); querySetCell(qMenuData, "label", "Forward");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 28); querySetCell(qMenuData, "parentId", 25); querySetCell(qMenuData, "label", "Up One Level");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 29); querySetCell(qMenuData, "parentId", 25); querySetCell(qMenuData, "label", "Resource");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 30); querySetCell(qMenuData, "parentId", 0); querySetCell(qMenuData, "label", "Search");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 31); querySetCell(qMenuData, "parentId", 30); querySetCell(qMenuData, "label", "Search...");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 32); querySetCell(qMenuData, "parentId", 30); querySetCell(qMenuData, "label", "File");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 33); querySetCell(qMenuData, "parentId", 30); querySetCell(qMenuData, "label", "text");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 34); querySetCell(qMenuData, "parentId", 33); querySetCell(qMenuData, "label", "Workspace");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 35); querySetCell(qMenuData, "parentId", 33); querySetCell(qMenuData, "label", "Project");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 36); querySetCell(qMenuData, "parentId", 33); querySetCell(qMenuData, "label", "File");
queryAddRow(qMenuData); querySetCell(qMenuData, "ID", 37); querySetCell(qMenuData, "parentId", 33); querySetCell(qMenuData, "label", "Working Set");
</cfscript>
<cfsavecontent variable="sMenus">
<cfset sCfO = "<" & "cf">
<cfset sCfC = "</" & "cf">
<cfoutput>
#sCfO#menu type="horizontal">
<cfquery name="qMenus" dbType="query">
select id, parentId, label
from qMenuData
where parentId = 0
order by id
</cfquery>
<cfloop query="qMenus">
#sCfO#menuitem display="#label#" href="#CGI.script_name#?id=#id#" name="sub_#parentId#_#id#">
#getSubmenus(menuData=qMenuData, parentId=id)#
#sCfC#menuitem>
</cfloop>
#sCfC#menu>
</cfoutput>
</cfsavecontent>
<cffile action="write" file="#sMenuFilePath#" output="#sMenus#">
</cfif>
<cfinclude template="./#sMenuFile#">
<cffunction name="getSubmenus" returntype="string">
<cfargument name="menuData" type="query" required="true">
<cfargument name="parentId" type="numeric" required="true">
<cfset var qSubs = false>
<cfset var sCfO = "<" & "cf">
<cfset var sCfC = "</" & "cf">
<cfset var sMenus = "">
<cfquery name="qSubs" dbtype="query">
select id, parentId, label
from menuData
where parentId = <cfqueryparam value="#arguments.parentId#" cfsqltype="cf_sql_integer">
order by id
</cfquery>
<cfsavecontent variable="sMenus">
<cfoutput>
<cfloop query="qSubs">
#sCfO#menuitem display="#label#" href="#CGI.script_name#?id=#id#" name="sub_#parentId#_#id#">
#getSubmenus(menuData=menuData, parentId=id)#
#sCfC#menuitem>
</cfloop>
</cfoutput>
</cfsavecontent>
<cfreturn sMenus>
</cffunction>
--
Adam
Copy link to clipboard
Copied
Wow, you are incredible! I don't know how I can thank you enough.
Copy link to clipboard
Copied
I found a better way to do this. Apparently whilst one cannot abstract <cfmenuitem> calls out into UDFs, one can abstract them out into custom tags.
So change the bottom bit (after the query creation) of that file to this:
<cfmenu type="horizontal">
<cfquery name="qMenus" dbType="query">
select id, parentId, label
from qMenuData
where parentId = 0
order by id
</cfquery>
<cfloop query="qMenus">
<cfmenuitem display="#label#" href="#CGI.script_name#?id=#id#" name="sub_#parentId#_#id#">
<cf_tag_dosubs menuData="#qMenuData#" parentId="#id#">
</cfmenuitem>
</cfloop>
</cfmenu>
And use this for the tag_dosubs.cfm file:
<cfparam name="attributes.menuData" type="query">
<cfparam name="attributes.parentId" type="numeric">
<cfset menuData = attributes.menuData>
<cfquery name="qSubs" dbtype="query">
select id, parentId, label
from menuData
where parentId = <cfqueryparam value="#attributes.parentId#" cfsqltype="cf_sql_integer">
order by id
</cfquery>
<cfloop query="qSubs">
<cfmenuitem display="#label#" href="#CGI.script_name#?id=#id#" name="sub_#parentId#_#id#">
<cf_tag_dosubs menuData="#attributes.menuData#" parentId="#id#">
</cfmenuitem>
</cfloop>
That makes it fully run-time dynamic. That said... it all depends on how often your menu data changes as to whether you actually want/need this processing overhead all the time. I'd still consider generating the menus at "change time" (like when the data changes), not necessarily runtime.
--
Adam
Copy link to clipboard
Copied
Testtttt