Code Monkey home page Code Monkey logo

Comments (5)

sirgallifrey avatar sirgallifrey commented on June 16, 2024 1

@cmar0027 I'm doing something similar on my little project I'm using to learn go and templ.
I have components call a function that will register which dependencies they need, and at the end, it will render the script tag for each dependency that was flagged.

It's in essence the same idea proposed by a-h.

I have a Middleware that will add a pointer to the context. (This is using fiber, but should work the same for a normal context, with just a little more code.)
https://github.com/sirgallifrey/gofinance/blob/main/web/view/ctx_middleware.go#L7

this is my struct, you can see I have useful things like the path and the environment, and also a map for the dependencies, if they are not used at all, it will be nil and not consume any memory.
that's why I think you don't need to setup a different context for each of the things you want to expose. Just add all at this one view context, unless you really want to separate some concerns that should not be together...
https://github.com/sirgallifrey/gofinance/blob/main/web/view/ctx.go#L9

you can see that I have some nice functions here to extract the values from the context here so I don't have to write this code inside the templates.

here I have one component flagging that HTMX is required and then it will be rendered here if it was actually required at least once.

Here is the script template, see how it gets the contexts and decides to render or not the script

You can't just execute functions inside a normal templ Component, so that function I have to flag the dependencies is actually a component that returns nil but gives me the chance to put the info in my struct

Hope that helps.

from templ.

joerdav avatar joerdav commented on June 16, 2024 1

The introduction of arbitrary go in templates should make this possible: https://templ.guide/syntax-and-usage/raw-go

I think now it is just a case of using Go to share this state upwards if that is what you would like!

from templ.

a-h avatar a-h commented on June 16, 2024

I haven't had time to fully digest this, but it is possible to update the context from within a templ component, if the value is already available in the context to mutate, as per: https://go.dev/play/p/GtvBP0VICp4

package main

import (
	"bytes"
	"context"
	"fmt"
	"html"
	"io"

	"github.com/a-h/templ"
)

func Footnote(name, href string) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		// Get the footnote context and update it.
		fc := ctx.Value(FootnoteContextKey{}).(*FootnoteContext)
		fc.Footnotes = append(fc.Footnotes, FootnoteData{Name: name, Href: href})

		// Render the footnote name immediately.
		io.WriteString(w, html.EscapeString(name)+"\n")
		return nil
	})
}

type FootnoteContextKey struct{}

type FootnoteData struct {
	Name string
	Href string
}

type FootnoteContext struct {
	Footnotes []FootnoteData
}

func PrepareFootnoteContext(ctx context.Context) context.Context {
	return context.WithValue(ctx, FootnoteContextKey{}, &FootnoteContext{
		Footnotes: make([]FootnoteData, 0),
	})
}

func FootnoteFooter() templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
		fc := ctx.Value(FootnoteContextKey{}).(*FootnoteContext)
		io.WriteString(w, "\n<footer>\n")
		for i, f := range fc.Footnotes {
			io.WriteString(w, fmt.Sprintf("<div>\n\t%d <a href=\"%s\">%s</a>\n</div>\n", i+1, html.EscapeString(f.Href), html.EscapeString(f.Name)))
		}
		io.WriteString(w, "</footer>")
		return nil
	})
}

func main() {
	ctx := context.Background()
	ctx = PrepareFootnoteContext(ctx)

	w := bytes.NewBuffer(nil)
	Footnote("First footnote", "#first").Render(ctx, w)
	Footnote("Second footnote", "#second").Render(ctx, w)
	FootnoteFooter().Render(ctx, w)

	fmt.Println(w.String())
}

The output of the above is:

First footnote
Second footnote

<footer>
<div>
        1 <a href="#first">First footnote</a>
</div>
<div>
        2 <a href="#second">Second footnote</a>
</div>
</footer>

from templ.

cmar0027 avatar cmar0027 commented on June 16, 2024

Thank for your reply, I think this works, although having to setup the context value beforehand for each request could become a bit annoying if there are many specific contexts that need to be setup.

But I think this example could be turned into something very similar to what I was originally asking for by just storing a pointer to a second context inside the original one. Then anyone would be able to access it and modify it as needed.

from templ.

joerdav avatar joerdav commented on June 16, 2024

I could be missing the point here but this suggestion seems to be in conflict with what ctx in Go tries to achieve. I think that if you are wanting to do this then the best option is to create a context value which is defined higher up and can be modified via reference, and this should be supported already. That could be a workaround.

However, in my personal opinion this would indicate to me that there is too much logic in my templates. I would likely create some view models for my page that contain all of the footnotes, pre-calculated before I even call into a templ component. Which may be more code and not fit with your current pattern.

Final suggestion is to not use ctx at all, you could define your own footnotes store type that gets passed down through templates, this would be more explicit as to how things are working. I hope some of these ideas help!

from templ.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.