Templates
O Kyrux usa herança de templates no estilo Django. Os templates ficam em apps/<nome>/templates/.
Convenção de variáveis
| Forma | Origem | Exemplo |
|---|---|---|
{{ .titulo }} |
Dados da view | map[string]any{"titulo": "..."} |
{{ AppName }} |
Framework | Nome do app no .env |
{{ Version }} |
Framework | Versão do app |
{{ Env }} |
Framework | development / production |
{{ Addr }} |
Framework | 0.0.0.0:8000 |
{{ GoVersion }} |
Framework | go1.22.3 |
{{ url "nome" }} |
Framework | Resolve a URL pelo nome |
{{ statics "path/arquivo.css" }} / {{ statics "app" "path/arquivo.css" }} |
Framework | Resolve URL de arquivo estático (global ou por app) |
{{ csrf_token }} |
Framework | Input hidden de segurança |
Herança de templates
apps/blog/templates/base.html:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>{% block "title" %}{{ AppName }}{% endblock "title" %}</title>
<link rel="stylesheet" href="{{ statics "blog" "styles/style.css" }}">
</head>
<body>
{% include "partials/navbar.html" %}
<main>
{% block "content" %}{% endblock "content" %}
</main>
{% block "scripts" %}{% endblock "scripts" %}
</body>
</html>
apps/blog/templates/post.html:
{% extends "base.html" %}
{% block "title" %}{{ .post.Titulo }} — {{ AppName }}{% endblock "title" %}
{% block "content" %}
<article>
<h1>{{ .post.Titulo }}</h1>
<p>{{ .post.Conteudo }}</p>
</article>
{% endblock "content" %}
{% block "scripts" %}
<script src="{{ statics "blog" "scripts/post.js" }}"></script>
{% endblock "scripts" %}
Diretivas de template
| Diretiva | Descrição |
|---|---|
{% extends "base.html" %} |
Herda de outro template |
{% block "nome" %}...{% endblock "nome" %} |
Define/sobrescreve um bloco |
{% include "partials/nav.html" %} |
Inclui um template parcial |
Renderizar fragmento (para Realtime)
html, err := render.Partial("blog", "partials/lista.html", map[string]any{
"posts": posts,
})
Context Processors — variáveis globais nos templates
Um ContextProcessor é uma função que adiciona variáveis ao contexto de todo template de um app — sem precisar passá-las manualmente em cada view.
import "kyrux/core/render"
// Processor global: disponível em TODOS os templates de TODOS os apps
render.AddDefaultProcessor(func(ctx *router.Context) map[string]any {
return map[string]any{
"site_nome": "Meu Site",
"ano_atual": time.Now().Year(),
}
})
Acesso no template (com ponto):
<footer>{{ .site_nome }} — {{ .ano_atual }}</footer>
Processors são acumulados: cada chamada a AddDefaultProcessor adiciona um novo — não substitui os anteriores.
Funções personalizadas nos templates
Registre funções Go para uso em todos os templates via render.AddFunc:
import "kyrux/core/render"
// Registrar antes de qualquer render (ex: no init() do app ou no bootstrap)
render.AddFunc("formatarData", func(t time.Time) string {
return t.Format("02/01/2006")
})
render.AddFunc("upper", strings.ToUpper)
Uso no template:
<span>{{ .post.CriadoEm | formatarData }}</span>
<h1>{{ .titulo | upper }}</h1>
Arquivos estáticos
Existem dois tipos de estáticos, ambos servidos automaticamente em /statics/ pelo mesmo handler:
- Por app: ficam em
apps/<nome>/statics/. - Globais: ficam em
statics/na raiz do projeto, compartilhados entre todos os apps.
Use a função statics no template para gerar a URL — todos os argumentos passados são concatenados (com /) para formar o caminho.
Sintaxe:
{{ statics "caminho/arquivo.ext" }} <!-- estático global -->
{{ statics "nome-do-app" "caminho/arquivo.ext" }} <!-- estático do app -->
Como a resolução funciona: para cada requisição em /statics/<caminho>, o framework tenta, nessa ordem:
statics/<caminho>na raiz do projeto.- Se não encontrar, usa o primeiro segmento de
<caminho>como nome de app e busca emapps/<app>/statics/<resto>.
Por isso, ao chamar {{ statics "blog" "styles/base.css" }}, a URL gerada inclui o nome do app (/statics/blog/styles/base.css) — isso é necessário para o fallback por app funcionar. Já {{ statics "styles/base.css" }} (sem nome de app) gera /statics/styles/base.css, resolvido direto na pasta raiz.
Exemplos — estático por app:
<!-- CSS e JS -->
<link rel="stylesheet" href="{{ statics "blog" "styles/base.css" }}">
<script src="{{ statics "blog" "scripts/app.js" }}"></script>
<!-- Imagem fixa -->
<img src="{{ statics "blog" "images/logo.png" }}" alt="Logo">
<!-- Imagem com nome vindo do banco de dados -->
<img src="{{ statics "blog" "uploads/" .UserPhoto }}" alt="Foto">
<!-- Caminho completamente dinâmico -->
<img src="{{ statics "blog" .ImagePath }}" alt="Imagem">
<!-- Combinando prefixo, subpasta e variável -->
<img src="{{ statics "blog" "uploads/" .Category "/" .FileName }}" alt="Arquivo">
URLs geradas:
/statics/blog/styles/base.css
/statics/blog/uploads/avatar.jpg
Estrutura de pastas por app:
apps/
└── blog/
└── statics/
├── styles/ ← CSS
├── scripts/ ← JavaScript
└── images/ ← Imagens e outros assets
Estáticos globais (compartilhados entre todos os apps, resolvidos com prioridade sobre os estáticos por app):
statics/ ← raiz do projeto
├── styles/
├── scripts/
└── images/
Exemplos — estático global:
<link rel="stylesheet" href="{{ statics "styles/global.css" }}">
<!-- gera /statics/styles/global.css, resolvido em statics/styles/global.css -->
<img src="{{ statics "images/logo.png" }}" alt="Logo">
Use estáticos globais para arquivos compartilhados entre apps (ex: reset CSS, framework JS, fontes, ícones do site); use estáticos por app para arquivos exclusivos de um app.