a-h / templ

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

Passing golang variables to javascript #944

Open farhank3389 opened 1 month ago

farhank3389 commented 1 month ago

I have a templ block defined like this:

templ test(teststring string) {
    <script>
        console.log({ "the value of teststring is " + teststring });
    </script>
}

However this does not work. The HTML it generates looks like this:


    <script>
        console.log({ "the value of teststring is " + teststring });
    </script>

I'm not sure if I'm doing something completely wrong here as this is my first time using templ. It seems to me that something like this should work. I had a look at the Using JavaScript with templ docs page and it says to not use the script template method of doing this, which does work for me. I'm not sure how I can do the same thing using script tags as the docs say.

pharrisee commented 1 month ago

There is some documentation on passing server side data to client scripts:

https://templ.guide/syntax-and-usage/script-templates#pass-server-side-data-to-the-client-in-a-script-element

I also use a slightly different pattern on occasion, but tbh I'm not too happy with either way of doing it and much preferred the now deprecated script templates (https://templ.guide/syntax-and-usage/script-templates#script-templates):

templ test(teststring string) {
    <script teststring={teststring}>
        const teststring=document.currentScript.getAttribute("teststring");
        console.log({ "the value of teststring is " + teststring });
    </script>
}
farhank3389 commented 1 month ago

ah yeah that does look like what I need. And agreed, I prefer the script template method too.

a-h commented 1 month ago

I can see why you like the script templates. There might be some middle ground to hit in the future, but need to control the scope of what we're working on in the short term - i.e. need to prioritise, and I think scripts are ... OK.

The problem with the script templates is really around how the initial design has boxed us in a bit. I don't think there's a clear way for us to expand on them to do async JS templates, add typescript, introduce formatting or LSP features etc. so it's more of a pause on progressing that feature direction than anything else.

Although... in August we did add a pretty cool new feature to it: https://github.com/a-h/templ/commit/ef4dde62b8cd6e53ed55b15b7443f292cc8d4d76

The need to pass variables from the Go world to the JS world won't go away, but I was looking at introducing templ variables directly into <script> tags instead. Combined with templ.Once which limits rendering of the block to once per HTTP request, it would provide all the same features, but fit better into the ecosystem.

However, I would need to find a way to slot Go variables into the JS code, by parsing the content, or using a helper function.

Just typing this out has put a new idea in my head.

type Data struct {
  X int `json:"x"`
  Y int `json:"y"`
}

templ Component(d Data) {
  <script type="text/javascript" data-templ-jsvar-name={ d }>
    alert(name.x * name.y)
  </script>
}

If there's a data-templ-jsvar-* attribute, templ could generate a JS closure that includes the -name as a variable, e.g.:

  <script type="text/javascript">
    let name = { x: 1, y: 3 };
    alert(name.x * name.y)
  </script>

However, that would mean that the variable wouldn't exist in the dev environment while you're coding, which might trigger linters, since they wouldn't know how to resolve the values.

templ could generate a JS file containing type definitions (classes?) for the data, e.g.: if you add an attribute for data-templ-jsvar-name it takes the name of the Go type (Data) and creates a JS class that matches in another file.

class Data {
  constructor(o) {
    //TODO: Get the data from a JSON variable in the attribute of the parent script tag.
    this.x = x;
    this.y = y;
  }
}
templ Component(d Data) {
  <script type="text/javascript" data-templ-jsvar-name={ d }>
    let d = new Data(this); // The Data class was auto-generated by templ.
    alert(d.x * d.y)
  </script>
}

Thoughts? @joerdav?

gotzmann commented 1 month ago

Just typing this out has put a new idea in my head.

Looks cool! Good to have the feature, just shorten the name of tag maybe? Like data-templ-let or something like this?

nanvenomous commented 6 days ago

Just going to humbly ask: please do not fully remove the script name() templ components until <script data-templ-let-name={} > tag variable are implemented.

I'm looking at a bit of a migration when this happens (maybe others?). Data transfer go -> js is too useful of a feature.

a-h commented 6 days ago

Stability is important. Would never want to break people's code.

a-h commented 6 days ago

Also, if we really wanted to remove something, we'd likely add an automatic migration feature, plus keep the old method around for at least two Go release cycles.

joerdav commented 5 days ago

And also, I think we would want to make sure that it was desireable to migrate a way. The alternative should be a smoother experience, I don't think we want to force a move to a unfinished solution. Once we have a proper solution hopefully people will want to move to it because it is easier.