gofiber/template

🐛 [Bug]: Template layout always show last loaded page.

jiuliyemingzhi opened this issue · 5 comments

Bug Description

Template layout always show last loaded page, I defined a layout template and some normal template they have similar struction.

<!DOCTYPE html>
<html lang="en">
<head>
	<title>{{.Title}}</title>

	<link rel="stylesheet" type="text/css" href="all.min.css">
	{{block "page-css" .}}{{end}}
</head>

<body>

{{block "main" .}}{{end}}

<script src="assets/js/all.min.js"></script>
{{block "page-js" .}}{{end}}
</body>
</html>

How to Reproduce

https://github.com/jiuliyemingzhi/fiber-tmpl-problem
1.go run main.go
2.click to open 127.0.0.1:3000 in browser

Expected Behavior

I think this page should show current url target page.

Template package Version

v2.1.1

Code Snippet (optional)

package main

import "github.com/gofiber/template/%package%"

func main() {
  	engine := html.New(conf.StaticPath, ".html")
	engine.Debug(true)
	engine.Reload(conf.Debug)
	app := fiber.New(fiber.Config{
		Views:     engine,
		BodyLimit: 2 * 1024 * 1024,
	})
}

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my problem prior to opening this one.
  • I understand that improperly formatted bug reports may be closed without explanation.

pls use our layout approach with the {{embed}} tag https://github.com/gofiber/template/tree/master/html#basic-example @jiuliyemingzhi

Thinks,I know this but I have a more complex usage scenarios。So {{embed}} can't meet the demand。 Like this code

<!DOCTYPE html>
<html lang="en">
<head>
	<title>{{.Title}}</title>

	<link rel="stylesheet" type="text/css" href="all.min.css">
	{{block "page-css" .}}{{end}}
</head>

<body>

{{block "main" .}}{{end}}

<script src="assets/js/all.min.js"></script>
{{block "page-js" .}}{{end}}
</body>
</html>

To clarify. This is not a caching problem where the last rendered template is being re-rendered. The issue is that "main" has been redefined in various files in ./templates, and all of them are being parsed by the views engine in app.

For instance, I added a file to your example app:

Image 2024-03-10 at 3 32 PM

I see two possible solutions to address the use case reported:

  1. Use the {{embed}} tag to load different content in a layout with the Fiber Render() function. Define other dynamic, used in each template, content in go not the html templates.

  2. Use html/template independently of Fiber to limit which file is parsed by the template, which will be slower without caching etc, but prevents "main" from being redefined by other files.

Example for solution 2:

package main

import (
	"bytes"
	"html/template"
	"log"
	"path/filepath"

	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New(fiber.Config{})

	app.Get("/", func(ctx *fiber.Ctx) error {
		return ctx.Redirect("/index2.html")
	})
	app.Get("/:page", func(ctx *fiber.Ctx) error {
		page := ctx.Params("page", "index1.thml")

		// Clean the page parameter and join it with the template directory
		pagePath := filepath.Join("template", filepath.Clean("/"+page))

		tmpl, err := template.ParseFiles("template/layout.html", pagePath)
		if err != nil {
			return ctx.Status(404).SendString("404 Not Found")
		}

		var buf bytes.Buffer

		err = tmpl.Execute(&buf, map[string]interface{}{})
		if err != nil {
			log.Println("Template execution error:", err)
			return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
		}
		ctx.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
		return ctx.Status(fiber.StatusOK).Send(buf.Bytes())
	})
	log.Fatal(app.Listen(":3000"))
}

@jiuliyemingzhi hope that helps.

@ReneWerner87, I think that this is not a bug, but rather an implementation detail that reflects the behaviour of the html/template ParseFiles() function. To avoid confusion, it would be helpful to update the documentation to explain that templates read during Load() are loaded when the app is initialized, and the use case mentioned by @jiuliyemingzhi is not supported.

To clarify. This is not a caching problem where the last rendered template is being re-rendered. The issue is that "main" has been redefined in various files in ./templates, and all of them are being parsed by the views engine in app.

For instance, I added a file to your example app:

Image 2024-03-10 at 3 32 PM

I see two possible solutions to address the use case reported:

  1. Use the {{embed}} tag to load different content in a layout with the Fiber Render() function. Define other dynamic, used in each template, content in go not the html templates.
  2. Use html/template independently of Fiber to limit which file is parsed by the template, which will be slower without caching etc, but prevents "main" from being redefined by other files.

Example for solution 2:

package main

import (
	"bytes"
	"html/template"
	"log"
	"path/filepath"

	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New(fiber.Config{})

	app.Get("/", func(ctx *fiber.Ctx) error {
		return ctx.Redirect("/index2.html")
	})
	app.Get("/:page", func(ctx *fiber.Ctx) error {
		page := ctx.Params("page", "index1.thml")

		// Clean the page parameter and join it with the template directory
		pagePath := filepath.Join("template", filepath.Clean("/"+page))

		tmpl, err := template.ParseFiles("template/layout.html", pagePath)
		if err != nil {
			return ctx.Status(404).SendString("404 Not Found")
		}

		var buf bytes.Buffer

		err = tmpl.Execute(&buf, map[string]interface{}{})
		if err != nil {
			log.Println("Template execution error:", err)
			return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
		}
		ctx.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
		return ctx.Status(fiber.StatusOK).Send(buf.Bytes())
	})
	log.Fatal(app.Listen(":3000"))
}

@jiuliyemingzhi hope that helps.

@ReneWerner87, I think that this is not a bug, but rather an implementation detail that reflects the behaviour of the html/template ParseFiles() function. To avoid confusion, it would be helpful to update the documentation to explain that templates read during Load() are loaded when the app is initialized, and the use case mentioned by @jiuliyemingzhi is not supported.

Thanks! That solved my problem.

Yes right, the engine loads all files and renders them and then definitions with the same names are overwritten

That was the reason why I meant the embed layout

But right, with more explanations it offers more acceptance

Thx @sixcolors for the explanation