a-h / templ

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

Using external JS lib #106

Closed sloba-byte closed 1 year ago

sloba-byte commented 1 year ago

Hi,

I think this has potential, however I failed to find a way to use some external JS library?

For example, lets say => https://github.com/tradingview/lightweight-charts would there be a way to somehow use this with templ? Goal would be to still not write JS, or maybe do it minimally (write JS wrappers which are called from go?)

a-h commented 1 year ago

Thanks for trying templ!

The docs were only hinting at what's possible, so I've made it more explicit, and also included an example in the repo in the following commit: https://github.com/a-h/templ/commit/6d8379950cb21f9c48cec753afebe0cbeac7ee32

See here for an example: https://github.com/a-h/templ/tree/main/examples/external-libraries

I've also updated the docs here: https://templ.guide/syntax-and-usage/script-templates

Does that give you what you were looking for?

joerdav commented 1 year ago

On the goal of not writing JS, someone is always going to have to write some JS. Using what @a-h has documented someone could package and export lightweight-charts as a sort of "templ component library", which prevents others from having to write the JS.

sloba-byte commented 1 year ago

Thanks for the answer! I will try it out tomorrow no time today.

I look always to write bare minimum or no JS, as I don't like it: 1) Dynamically typed - usually slow to iterate and find bugs. 2) If you want types you need to add so much complexity and external tooling - again it's very poor experience

Usually, I am crafting rather simple UIs, reports etc read only. I prefer to do all the work in go.

I would love to get somehow type safety in "templ body() {

" part... Even some half measure would be rather useful. What are you thoughts @a-h @joerdav
joerdav commented 1 year ago

@sloba-byte did you take a look at the documentation @a-h added here: https://templ.guide/syntax-and-usage/script-templates/

sloba-byte commented 1 year ago

I tried it out and it works great!

My point is:

script graph(data []TimeValue) {
    const chart = LightweightCharts.createChart(document.body, { width: 400, height: 300 });
    const lineSeries = chart.addLineSeries();
    lineSeries.setData(data);
}

This is just a string, you can write what ever you want:

  1. It has no context about parameter passed (you can write lineSeries.setData(someParamName) and it will compile and execute)
  2. It has no context of JS

First thing sounds like easy to have ; Second is what I am looking for and that sounds like a hard thing.

a-h commented 1 year ago

Yes, that's right.

  1. A hard bit about JavaScript is that you can pass basically anything to anything! New variables can appear in scope too by loading additional scripts.

So, someParamName might exist, but it's defined in a remote file, e.g. <script src="https://example.com/someParamNameIsLoadedHere.js"></script> tag, or further up the page.

There's also security considerations around how data gets passed around between domains. The concept of the script template as seen in templ is informed by Google's SafeHTML project - https://github.com/google/safehtml - so templ uses JSON serialization to sanitize the data passed through to script templates.

  1. Yes, no context of JS. Maybe what you're looking for is more like Web Assembly, where Go is compiled for execution in the browser? https://golangbot.com/webassembly-using-go/
sloba-byte commented 1 year ago

Got it. Thanks for explanation. Not sure webAssembly is the right way for me.

Usually, I am crafting projects were they need some read only UI, to visualize whats going on. The UI is just a very small bit but it's important. Over the years I have tried many things, firstly I was doing it with large frameworks typescript node blabla all of that, but it was to heavy and to much work for the return.

Currently, I have this solution that I like to some extent (if I could get somehow JS type safety, or something close to it would be great):

  1. I just write everything in 1 HTML fajl for 1 page. I use live.js or some other method for hot reload
  2. In the JS part, I define test data I am using while developing. In the JS function I write something like:
    let data = testData //{{.}}
  3. After I am happy with UI, I write the go part to prepare the data and use template/html package to insert it in html directly. There is one more step before were I load the original html and replacing let data = testData //{{.}} with let data = {{.}}. This step gets rid of testData additionally.

PROS: I like this the best so far, I don't have to do much, setup node typescript and what ever use some framework like svelte/react. I just write plain old html/css/js and visually in browser.

CONS: No help with JS, although JS is kept at minimum still sometime you need to refactor it or add more and not having any feedback from editor is a bad experience.

BONUS: What I plan on doing for the future is to write a small simple tool which I can run once I finish with HTML part to extract from testData golang struct, so I don't make mistakes there or do a lot of copy/paste/change. A small generator.

I am looking to somehow, anyhow improve the JS experience. So thats why I looked at this project, and I am planing on checking out the JSDocs types but not sure if I will able to setup them and/or like the again rely on TS and some config.

a-h commented 1 year ago

In some work I've been doing, I've been using TypeScriptify to create TypeScript interfaces from Go structs. That then allows compile-time checks in TypeScript against the Go code definitions: https://github.com/tkrajina/typescriptify-golang-structs

sloba-byte commented 1 year ago

Thanks for the help and all the info. Good luck with the tool.

pezdel commented 1 year ago

Hi, so I'm very new to this library and was playing around with the lightweight chart example but wanted to have a hx-get -> update the chart data. I got the below example to work but not sure if there is a better way to get it to work with tools from the library.

Thanks

Not a lot of changes from the original, added comments at the spots.

script onLoad(data []TimeValue) {
    //changed to lets
    chart = LightweightCharts.createChart(document.body, { width: 300, height: 400 });
    lineSeries = chart.addLineSeries();
    lineSeries.setData(data)
}
templ page(data []TimeValue) {
    <script>
              let lineSeries = {} //NEW
         </script>
    <html>
        <head>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
            <title>Graphs</title>
            <script src="https://unpkg.com/htmx.org@1.9.6"></script>
            <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
        </head>
        <body id="body" onload={ onLoad(data) }>
            <button hx-get="/data">Click me</button> //NEW
        </body>
    </html>
}

New Route

mux.HandleFunc("/data", func(w http.ResponseWriter, r *http.Request) {
      data := []TimeValue{}   //just empted it out since its just a example
      jsonData, _ := json.Marshal(data)
      jsCode := fmt.Sprintf("<script>lineSeries.setData(%s)</script>", jsonData)
      w.Write([]byte(jsCode))
}
a-h commented 1 year ago

In this case, I don't think it makes sense to use HTMX to do the work.

With HTMX you'd be rendering a HTML script tag containing JSON just so that you can pass some JSON to client-side JavaScript, so I think it makes sense to use the browser's fetch API to get the JSON directly - https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API and update the variable.

i.e. something like this. (I haven't tested it all, don't expect it to work, it's just an example).

templ page(data []TimeValue) {
    <script>
              let lineSeries = {} //NEW
         </script>
    <html>
        <head>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
            <title>Graphs</title>
            <script src="https://unpkg.com/htmx.org@1.9.6"></script>
            <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
        </head>
        <body id="body" onload={ onLoad(data) }>
            <button onclick="() => fetch("/data").then((result) => lineSeries = result)">Click me</button> //NEW
        </body>
    </html>
}
a-h commented 1 year ago

By the way, there's a Slack channel in the Go #gophers Slack called templ for general discussions.

eussam commented 10 months ago

By the way, there's a Slack channel in the Go #gophers Slack called templ for general discussions.

Hi @a-h , any reason why this info is not part of the https://templ.guide/media/ section of the doc ?

a-h commented 10 months ago

No reason, just hasn't been done.

Probably needs a new page in the docs, something like "Getting help" which links to the Slack channel, Github discussions, and details how to raise a good issue.

a-h commented 10 months ago

I've updated the docs to add a new page in https://github.com/a-h/templ/commit/bedd9c4e374975fa626e42cc5d7fe26997077837