Kyrux
Voltar

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:

  1. statics/<caminho> na raiz do projeto.
  2. Se não encontrar, usa o primeiro segmento de <caminho> como nome de app e busca em apps/<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.