NOTÍCIA

Custom tags aninhadas

Introdução

Custom tags são pedaços de código que podem ser chamados a partir
de diversas aplicações. Assim como funções (UDFs) e componentes
(CFCs), elas permitem que você encapsule código. No entanto, elas
diferem das funções em como são definidas e chamadas e também
em como compartilham dados com a página de chamada. Até o lançamento
do ColdFusion MX, as custom tags eram as “pedras fundamentais”
para a maioria das aplicações. Os CFCs ainda não existiam e as
UDFs não podiam chamar tags. Uma custom tag é simplesmente uma
página CFML. O que a torna diferente de qualquer outra página,
é a forma que ela é armazenada e referenciada a partir de outras
páginas. Dados podem ser retornados pelas custom tags, embora
não tão elegantemente como pelos CFCs. As custom tags podem ser
aninhadas umas nas outras e compartilharem dados entre si. Desta
forma, podemos criar, por exemplo, tags
<cf_table>, <cf_tr> e <cf_td>
que substituam as tags HTMLHTML45 conteúdosA importância do HTML e CSS para quem trabalha com UI Design e Design SystemProduto & UX · dez 2024Como hostear seu site HTML gratuitamente com GitHub PagesDev (Back & Front) · jun 2025SQL Server – Como criar um versionamento de código das suas Stored Procedures em HTML e com comentários da alteraçãoData · nov 2020Ver tudo em Dev (Back & Front) <table>,<
tr> e < td> aplicando a estas características
desejadas.

Implementação

Neste texto utilizaremos as custom tags <cf_BaseCustomTag>
e <cf_NestedCustomTag>. Utilizaremos ainda a página
test.cfm que fará as chamadas às duas custom tags.

Os três arquivos apresentam códigos
bastante simples, de modo a podermos verificar o aninhamento das
tags. Vamos dar uma olhada nos códigos destes arquivos:

test.cfm

<cf_BaseCustomTag>
<cfloop
index="i" from="1" to="5">
<cf_NestedCustomTag>
<cfoutput>#i#</cfoutput>
</cf_NestedCustomTag>
</cfloop>
</cf_BaseCustomTag>

Basicamente, neste arquivo, ocorre a chamada à
tag <cf_BaseCustomTag> e, internamente a ela – ou
aninhada, as sucessivas chamadas à tag <cf_NestedCustomTag>.
Como i varia de 1 a 5, teremos como conteúdo gerado para
esta tag somente os números de 1 a 5.

BaseCustomTag.cfm

<cfif ThisTag.ExecutionMode IS ‘Start’>

<cfelseif
ThisTag.ExecutionMode IS ‘End’>

<cfif
StructKeyExists(ThisTag,’assocAttribs’)>

<cfset
Attributes.NestedData = ThisTag.assocAttribs>
<cfdump var="#Attributes.NestedData#" label="Nested
Data">

</cfif>

</cfif>

No código de qualquer custom tag, temos à nossa
disposição a estrutura ThisTag. Suas chaves são
mostradas a seguir:

Variável
Descrição
ExecutionMode Modo
de execução: Start, End ou Inactive
HasEndTag A
custom tag possui uma end tag?
GeneratedContent O
código HTML gerado pela tag
AssocAttribs Uma
estrutura contendo os atributos das tags aninhadas

Então, analisando o código, vemos o acesso à
estrutura ThisTag.assocAttribs e posterior exibição
de seu conteúdo.

NestedCustomTag.cfm

<cfif ThisTag.ExecutionMode IS ‘Start’>

<!—
Associa os atributos locais à tag <CF_BaseCustomTag>
—>
<cfassociate basetag="CF_BaseCustomTag">

<cfelseif ThisTag.ExecutionMode IS ‘End’>

<cfset
Attributes.content = ThisTag.GeneratedContent>
<cfdump var="#Attributes#">
<!— Elimina o conteúdo para evitar outputs —>
<cfset ThisTag.GeneratedContent = "">

</cfif>

A tag CFML <Cfassociate> permite que a tag
<cf_BaseCustomTag> tenha disponível em ThisTag.assocAttribs
os atributos de <cf_NestedCustomTag>.

Em ThisTag.GeneratedContent, esperamos encontrar,
para cada chamada, os números de 1 a 5 (conforme visto
em test.cfm). De fato, ao executarmos o test.cfm, temos o seguinte
output fornecido por <cf_NestedCustomTag>.

Resumindo: no nosso exemplo, test.cfm chama 5 vezes
a tag <cf_NestedCustomTag> passando como conteúdo
os números de 1 a 5. Esta, por sua vez, passa este conteúdo
à tag <cf_BaseCustomTag> através da tag CFML
<CFassociate>.

Um ‘Probleminha Básico’
no ColdFusion MX

Conforme visto anteriormente, a tag <cf_BaseCustomTag> irá
realizar um dump nos dados aninhados. O quê deveríamos
esperar? Ora, o mesmo output anterior, dentro de um array de 5
posições. Ou seja:

No entanto, a partir do ColdFusion MX, o output
gerado é:

Ou seja, o conteúdo gerado em <cf_NestedCustomTag>
é correto, porém quando passado à tag <cf_BaseCustomTag>,
ele se perde sendo sucessivamente sobrescrito. O quê fazer?

Análise do Problema

Inicialmente lembremos alguns conceitos básicos do ColdFusion:

• Structures e Queries são passadas por referência

• Variáveis simples e Arrays são passados
por valor

O quê, de fato, significa isto?

Vejamos o seguinte exemplo:

Example1.cfm

<!— Cria a structure —>
<cfset stEmployee = structNew()>

<!— Define algumas chaves
—>
<cfset stEmployee.name = "John Doe">
<cfset stEmployee.age = "30">
<cfdump var="#stEmployee#" label="stEmployee">

<!— ‘Copia’ a structure
—>
<cfset stNewEmployee = stEmployee>

<!— Define algumas chaves
—>
<cfset stNewEmployee.name = "Mary Ann">
<cfset stNewEmployee.age = "25">
<cfdump var="#stNewEmployee#" label="stNewEmployee">
<cfdump var="#stEmployee#" label="stEmployee
depois da ‘Cópia’">

O output gerado é:

Ou seja, na ‘cópia’, o que foi
copiado foi a referência à área de memória
e não a área de memória propriamente dita.
Assim, qualquer referência feita a stNewEmployee referencia
stEmployee. Ambas são ponteiros para a mesma área
de memória.

Alterando a ‘cópia’ <cfset
stNewEmployee = stEmployee> para <cfset stNewEmployee =
duplicate(stEmployee)>, temos o efeito desejado:

Através da função duplicate(), temos dois
ponteiros apontando para áreas distintas de memória.

E as custom tags aninhadas?

No nosso problema, o comportamento é parecido. Vejamos:
structures são sobrescritas à medida que novas vão
sendo ‘criadas’. Algo sugere que, na geração
do código Java, este conceito básico não
está sendo levado em conta.

Analisemos um outro exemplo:

Example2.cfm

<cfset arrMyArray = arrayNew(1)>
<cfset stTemp = structNew()>

<cfloop index="i"
from="1" to="5">
<!— Define uma chave —>
<cfset stTemp.content = i>
<!— Salva a structure na posiçào i do
array —>
<cfset arrMyArray[i] = stTemp>
</cfloop>

<cfdump var="#arrMyArray#"
label="arrMyArray">

Cuja execução fornece:

De fato, esta situação em muito se
assemelha ao nosso problema de aninhamento de custom tags.

Vejamos: a structure stTemp é criada fora
do loop e tem sua chave content modificada a cada passada do loop.

Ao atribuirmo-la à cada posição
i do array, estamos fazendo aquela ‘cópia’
mostrada anteriormente. Ou seja, estamos apenas copiando a referência
e não criando uma área de memória nova. Conclusão:
estamos sobrescrevendo a structure a cada iteração
do loop. Exatamente como no aninhamento de custom tags.

Novamente, se trocarmos a atribuição
<cfset arrMyArray[i] = stTemp> por <cfset arrMyArray[i]
= duplicate(stTemp)>, teremos o ‘problema’ corrigido.

Corrigindo. Ou melhor, Contornando…

Nos exemplos mostrados, o problema era nosso, pois estávamos
deixando de lado um conceito básico e bastante importante
a respeito de structures. Mas, no caso das custom tags aninhadas,
o problema ocorre no código Java gerado. Sobre ele, não
podemos agir e corrigir o erro.

Como proceder?

O primeiro passo já foi dado. Coloquei o problema para
o Ben Forta, e a resposta foi que ele ainda não havia percebido
tal fato. Na nossa última conversa, sugeri a ele que atentasse
para o fato exposto nos exemplos acima. Vamos, então, aguardar
mais um updater.

Enquanto isso, o quê fazer?

Quase que por acidente, ao utilizar um par de custom tags notei
que o problema só acontece quando não é passado
nenhum atributo à custom tag contida no loop. Exatamente
como em test.cfm.
Ou seja, parece que o ColdFusion MX, ao encontrar uma primeira
chamada a uma custom tag sem nenhum atributo, traduz seu código
e aloca memória para ela.

Ao encontrar outra chamada, e esta também
sem nenhum atributo, ele pensa: “se não tem atributo,
nada mudou. Se nada mudou, não aloco memória”.
Aí o conteúdo gerado é sobrescrito ao anterior.
Então, se passarmos um atributo a ela, nossos problemas
terminaram.

Mas, e se a custom tag não possuir nenhum
atributo? Aí é que está o lado engraçado
da coisa. Se é que se pode ver graça neste problema.
Qualquer palavra pode ser passada como atributo. Melhor ainda,
nem valor precisa ter.

Se mudarmos o código de test.cfm para:

test.cfm

<cf_BaseCustomTag>
<cfloop
index="i" from="1" to="5">
<cf_NestedCustomTag
FOO>
<cfoutput>#i#</cfoutput>
</cf_NestedCustomTag>
</cfloop>
</cf_BaseCustomTag>

Reparem na adição do atributo foo à chamada
de <cf_NestedCustomTag>. Fazendo isto, o output gerado é:

E o problema fica, então, contornado.

Matérias especiais e reportagens conduzidas internamente pela Redação iMasters. Acompanhe no Twitter @imasters e no Instagram/Threads @portalimasters

Ver perfil