Skip to content
mobius1qwe edited this page Nov 10, 2026 · 1 revision

Home > Servir site e arquivos > Templates 🚧

Páginas HTML com templates (Mustache)

Important

Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.

Vale para a 2.0 · Delphi XE em diante e Lazarus/FPC 3.2 em diante · Nível: básico

Em uma frase: o TRALMustacheEngine monta uma página a partir de um modelo Mustache e dos seus dados (JSON, TDataSet, listas, objetos), sem lógica dentro do modelo.

Você vai precisar: o pacote RALModuleWebPackage (paleta RAL - Modules).

Ligando

Solte um TRALMustacheEngine no formulário (ou crie por código) e ajuste:

propriedade
TemplateRoot pasta dos arquivos de modelo; relativa ao executável se não for absoluta. Vazia: só AddTemplate e OnLoadTemplate
Extension acrescentada ao nome sem extensão (.html por padrão)
WatchFiles True (padrão): relê o arquivo quando a data muda; desligue em produção para poupar a checagem
Layout nome do modelo em que toda página respondida entra, no bloco content
Streamed envia a página aos poucos enquanto monta, em vez de inteira
AllowFilters liga os filtros {{nome | filtro}} (desligado por padrão)
OnFilter seus próprios filtros
OnLoadTemplate entrega o modelo de qualquer lugar (recurso, banco); roda antes de procurar o arquivo

Engine.AddTemplate('nome', 'texto') registra um modelo por código e vale mais que o arquivo. Engine.Reload descarta tudo o que foi compilado.

Respondendo uma rota

uses RALTemplate, RALJson;

Motor := TRALMustacheEngine.Create(Self);
Motor.TemplateRoot := 'modelos';
Motor.Layout := 'layout';
Servidor.CreateRoute('clientes', {$IFDEF FPC}@{$ENDIF}Listar);

procedure TForm1.Listar(ARequest: TRALRequest; AResponse: TRALResponse);
var
  Dados: TRALJSONObject;
begin
  Dados := TRALJSONObject.Create;   // ou um TDataSet, um TStringList, um objeto...
  try
    // ... preenche
    Motor.Render(AResponse, 'clientes', [Dados]);  // modelos/clientes.html dentro do layout
  finally
    Dados.Free;
  end;
end;

Render(AResponse, nome, [dados], status, usarLayout) responde como HTML; o status é 200 e o layout é usado, se houver, a menos que você diga o contrário. Com Streamed, a resposta sai em pedaços. Para outros destinos: Render(nome, [dados], AStream), RenderText(nome, [dados]) (texto, sem layout) e RenderSource(texto, [dados]) (sem guardar nada).

Lazarus/FPC: os handlers de evento (OnFilter, OnLoadTemplate) e de rota levam {$IFDEF FPC}@{$ENDIF} na frente, como em qualquer evento do RAL.

Que dados entram

Cada item do array é um contexto; o último é procurado primeiro, depois os anteriores (bom para um item de página por cima de dados comuns).

dado como o modelo o vê
TRALJSON objeto = mapa, array = lista; null e false pulam a seção
TDataSet lista de registros, cada um um mapa dos campos. Ponha RALTemplateDB no uses. Anda registro a registro, sem carregar tudo; o dataset fica no fim
TStrings lista das linhas e mapa dos pares Nome=Valor
TCollection lista dos itens
outro objeto mapa das propriedades published
TRALTemplateMap junta vários: TRALTemplateMap.Create.Add('titulo', 'Clientes').Add('lista', Query).AddBoolean('admin', True)

Os objetos precisam viver até o fim do render. Para um tipo seu, registre uma visão com RALRegisterTemplateData(MinhaClasse, MinhaVisao).

Sintaxe

Olá, {{nome}}              escapa o HTML
{{{html}}}  ou  {{&html}}  cru, sem escapar
{{cliente.endereco.cidade}} caminho com ponto
{{#itens}}{{nome}}{{/itens}}  seção: repete numa lista, entra num mapa, aparece se verdadeiro
{{^itens}}Nada{{/itens}}      invertida: só se vazio, nulo ou falso
{{#tags}}[{{.}}]{{/tags}}   o ponto é o item atual
{{! comentário }}
{{> rodape}}                parcial (outro modelo, no lugar)
{{=<% %>=}}                 troca os delimitadores

Herança: o modelo filho preenche os blocos do pai.

{{! base.html }}
<title>{{$titulo}}Site{{/titulo}}</title>
<body>{{$corpo}}{{/corpo}}</body>

{{! pagina.html }}
{{<base}}
  {{$titulo}}Clientes{{/titulo}}
  {{$corpo}}<h1>{{titulo}}</h1>{{/corpo}}
{{/base}}

Parcial dinâmico: {{>*nome}} abre o modelo cujo nome está na variável nome. Parcial inexistente rende vazio; um modelo pedido por Render que não existe levanta ERALException. Parciais e pais aninham até 100 níveis (recursão infinita é cortada).

O Layout é um modelo com {{$content}}...{{/content}}: a página responde dentro dele e pode preencher outros blocos ({{$title}}).

Filtros

Com AllowFilters := True: {{nome | trim | upper}}, em cadeia, com argumento entre aspas depois de :.

filtro faz
upper, lower, trim caixa e espaços
default:"x" x se o valor for vazio
json texto como string JSON (use com {{{ }}})
url percent-encoding
date:"dd/mm/yyyy" data ISO 8601 formatada (sem argumento, a data local)
number:"0.00" número formatado

Filtros próprios no OnFilter(Sender, AFilter, AArgument, var AValue, var AHandled); filtro desconhecido deixa o valor como está. Com AllowFilters desligado, a barra faz parte do nome da variável (e o resultado é vazio).

Escapes avulsos

Para montar HTML por código, a unit RALHtml oferece: RALHtmlEscape (texto entre tags), RALHtmlAttrEscape (valor de atributo), RALJsEscape (dentro de string JavaScript) e RALUrlEscape (segmento de URL ou query).

O que não tem

Funções lambda ({{#fn}}) não são executadas; calcule o valor antes e entregue pronto. Lógica (if, comparações) também não: é da natureza do Mustache.

Desempenho

O modelo é compilado uma vez e guardado; o arquivo só é relido quando a data muda (WatchFiles). Pode renderizar de várias threads ao mesmo tempo. Um TDataSet grande usa memória constante, porque cada registro vira uma visão que é liberada em seguida; com Streamed a página também sai sem ser montada inteira.

Segurança

  • {{x}} escapa o HTML; só {{{x}}} e {{&x}} deixam passar. Use cru apenas para conteúdo seu.
  • O nome de um modelo (inclusive o vindo de {{>*nome}}) nunca sai da TemplateRoot: nomes com .., : ou começando em / são recusados.
  • AllowFilters vem desligado: modelos que você não escreveu não ganham filtros.

Veja também

Começar

Servidor

MQTT 🚧

Cliente

Motores

Avançado

Ajuda

🚧 = em desenvolvimento, pode mudar

Clone this wiki locally