Skip to content

Echo Golang Template Issue: Content Not Changing on Different Routes #2751

Open
@alizainsoomro

Description

@alizainsoomro

Hi,

I am using the Echo framework in Golang to serve HTML templates, but I have an issue where the title updates correctly. However, the content remains the same for both the home (/) and about (/about) pages.

When I navigate to /about, the title changes to "About | Application", but the content is still the same as the home page (/) instead of showing the correct about page content.

Project Structure

/app
  /handlers
    home.go
    about.go
  /views
    renderer.go
  /templates
    /layouts
      main.html
    /pages
      index.html
      about.html
  main.go

Code Implementation main.go (Entry Point)

package main

import (
    "app/handlers"
    "app/views"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()

    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.Renderer = views.NewRenderer()

    e.GET("/", handlers.Home)
    e.GET("/about", handlers.About)

    e.Static("/static", "static")

    e.Logger.Fatal(e.Start(":8080"))
}

views/renderer.go (Template Renderer)

package views

import (
    "html/template"
    "io"
    "path/filepath"
    "strings"

    "github.com/labstack/echo/v4"
)

type TemplateRenderer struct {
    templates *template.Template
}

func NewRenderer() *TemplateRenderer {
    funcMap := template.FuncMap{
        "dict": func(values ...interface{}) map[string]interface{} {
            if len(values)%2 != 0 {
                panic("invalid dict call")
            }
            dict := make(map[string]interface{}, len(values)/2)
            for i := 0; i < len(values); i += 2 {
                key, ok := values[i].(string)
                if !ok {
                    panic("dict keys must be strings")
                }
                dict[key] = values[i+1]
            }
            return dict
        },
    }

    tmpl := template.New("").Funcs(funcMap)

    layoutGlob := filepath.Join("templates", "layouts", "*.html")
    pagesGlob := filepath.Join("templates", "pages", "*.html")

    tmpl = template.Must(tmpl.ParseGlob(layoutGlob))
    tmpl = template.Must(tmpl.ParseGlob(pagesGlob))

    normalizedTemplates := template.New("").Funcs(funcMap)
    for _, t := range tmpl.Templates() {
        if t.Tree == nil || t.Tree.Root == nil {
            continue
        }
        name := t.Name()
        if strings.HasPrefix(name, "pages/") {
            name = strings.TrimPrefix(name, "pages/")
        }
        name = strings.TrimSuffix(name, ".html")
        _, err := normalizedTemplates.New(name).Parse(t.Tree.Root.String())
        if err != nil {
            panic("Failed to normalize template names: " + err.Error())
        }
    }

    return &TemplateRenderer{
        templates: normalizedTemplates,
    }
}

func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
    return t.templates.ExecuteTemplate(w, name, data)
}

handlers/home.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo/v4"
)

func Home(c echo.Context) error {
    return c.Render(http.StatusOK, "index", map[string]interface{}{})
}

handlers/about.go

package handlers

import (
    "net/http"

    "github.com/labstack/echo/v4"
)

func About(c echo.Context) error {
    return c.Render(http.StatusOK, "about", map[string]interface{}{})
}

Templates

templates/layouts/main.html (Layout)

{{ define "layout" }}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ block "title" . }}{{ if .title }}{{ .title }} | Application{{ else }}Application{{ end }}{{ end }}</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <nav>
        <a href="/">Home</a> | <a href="/about">About</a>
    </nav>
    <main>
        {{ template "content" . }}
    </main>
</body>
</html>
{{ end }}

templates/pages/index.html

{{ template "layout" (dict "title" "Home") }}

{{ define "content" }}
    <h1>Welcome to the Home Page</h1>
    <p>This is a simple multi-page app with Echo.</p>
{{ end }}

templates/pages/about.html

{{ template "layout" (dict "title" "About") }}

{{ define "content" }}
    <h1>About</h1>
    <p>This is a simple multi-page app with Echo.</p>
{{ end }}

Expected Behavior / should render index.html inside layout/main.html. /about should render about.html inside layout/main.html.

Actual Behavior / works as expected. /about only updates the title but keeps the content from / (Home Page).

Possible Issue I suspect that {{ template "layout" (dict "title" "About") }} in about.html is calling layout directly and rendering "index.html" content instead of its own.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions