a-h / templ

A language for writing HTML user interfaces in Go.
https://templ.guide/
MIT License
7.11k stars 235 forks source link

Proposal: Load file as templ #795

Open dadcod opened 2 weeks ago

dadcod commented 2 weeks ago

I have a Vite setup where, at the end, JavaScript, CSS, and HTML files are produced. The HTML has the proper script tags, and everything is built using one command. To load the HTML as a template, I now have to use http/template like this:

        goTemplate,err := template.New("index.html").Funcs(template.FuncMap{}).ParseFiles("./fe/dist/index.html")
    if err != nil {
        log.Fatal(err)
        panic(err)
    }
    var sb = strings.Builder{}
    page.Render(ctx,&sb)
    return goTemplate, template.HTML(sb.String())

In the body, I can add a placeholder like this:

<body >
    {{ . }}
</body>

Here is the full repository: https://github.com/dadcod/frank-rank

I suggest having ParseFiles similar to http/template.

a-h commented 2 weeks ago

I'm not sure what you're asking for here.

Go's built-in templates work quite differently to templ.

templ has a "generator" which generates Go code files from templ files. These go code files are then compiled into your application, rather than parsed at runtime.

I've talked about how templ works a little bit in https://www.youtube.com/watch?v=EkK8Jxjj95s&t=2s (40m onwards, I think). There's a new talk coming (once the talks from Big Sky Dev Con are edited and put on YouTube) that goes into a lot more detail, but the https://github.com/a-h/templ/blob/main/CONTRIBUTING.md is a good place to start for now.

Do you have a specific issue or reason for having this function?

dadcod commented 2 weeks ago

TL;DR: I have a Vite-generated HTML file that I want to reuse.

For one part of the app, I have heavy client-side interactions, such as graphs, drag-and-drop, etc. For that, I want TypeScript support, npm, and the ability to test in isolation. So, I decided to use Vite for that, as well as for templ Tailwind classes. Now, instead of using separate esbuild and Tailwind commands, I have Vite handling everything. Plus, it takes care of importing the bundles. For the layout of the whole app, I want to use this Vite-generated index.html, and for everything else, I will use templ, htmx, and Tailwind.

P.S. I've made it work as I described through HTTP/templ. It's just not convenient. I will definitely watch the talk, and thank you for the great project that templ is! 😊

a-h commented 5 days ago

Hi, I'm afraid I still don't understand what the request is. Is it that you want to import the Vite generated HTML file? Is it that you want to output that generated HTML file in a Go web server? Not sure.

joerdav commented 4 days ago

I think I understand. You are looking for a way to use a generated layout file in templ. There is no way to do this currently with just templ as templ files need to be compiled to go. I think what you need is a custom component in your code:

You may want to refactor this to avoid global state and panics if possible but I hope you understand the general idea.

var layout templ.Component
func MustGetLayout() templ.Component {
    if layout != nil {
        return layout
    }
    l, err := ParseLayout("index.html")
    if err != nil {
        panic(err)
    }
    layout = l
    return l
}
func ParseLayout(filepath string) (templ.Component, error) {
    f, err := os.Open(filepath)
    if err != nil {
        return nil, err
    }
    layout, err := io.ReadAll(f)
    if err != nil {
        return nil, err
    }
    top, bottom, ok := bytes.Cut(layout, []byte("{{ . }}"))
    if !ok {
        return nil, fmt.Errorf("couldn't find children in layout")
    }
    return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
        if _, err := w.Write(top); err != nil {
            return err
        }
        if err := templ.GetChildren(ctx).Render(ctx, w); err != nil {
            return err
        }
        if _, err := w.Write(bottom); err != nil {
            return err
        }
        return nil
    }), nil
}

This would allow you to use your layout in other templates:

package views

templ Page() {
    @MustGetLayout() {
        ....
    }
}