-
-
Notifications
You must be signed in to change notification settings - Fork 225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow components to edit context key-value pairs #603
Comments
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> |
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. |
@cmar0027 I'm doing something similar on my little project I'm using to learn go and templ. 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.) 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. 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. |
I could be missing the point here but this suggestion seems to be in conflict with what 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! |
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! |
It would be nice if components were able to edit the context key value pairs in the Render() method.
I couldn't find a way to do this, but I think it would be nice for a variety of use cases, so I'd like to know if anyone has found a workaround or if other people are also looking forward to such a feature.
The following is an example use case to better illustrate what I'm talking about:
Imagine you have a page with a few footnotes. It's tedious and error prone to manually make sure each footnote is numbered correctly and in order, especially if there is a bunch of them and they change often.
Instead, it would be nice to just call a component, say
@FootNote
, to dinamically create and number footnotes wherever you want across the page, for example@FootNote("This is footnote 1")
would spit[1]
out, then@FootNote("This is footnote 2")
would spit[2]
out somewhere else, and so on.Finally, calling
@FootNotes()
at the end of the page would spit out the list of all footnotes in the page:It would be easy to implement something like this if components were able to edit context values, this way
@FootNote
could just add the given content to the context, then@FootNotes
would simply need to read and display that list.Here is an implementation that almost works:
The problem is that
@FootNotes()
doesn't output the full list of footnotes becausectx = context.WithValue(ctx, FOOT_NOTE_CTX_KEY, footNotes)
doesn't update the passed in context, just its local copy.I think something like this would enable a lot of interesting use cases for when a piece of content in the page dependes on another piece of content earlier in the page, that you either don't know in advanced, or are too lazy to manually setup before rendering.
The text was updated successfully, but these errors were encountered: