Skip to content
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

feat: add JSONString and JSONScript functions, update docs, mark templ script as legacy #745

Merged
merged 8 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.696
0.2.697
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ go tool covdata textfmt -i=./coverage/fmt,./coverage/generate,./coverage/version
go tool cover -func coverage.out | grep total
```

### test-cover-watch

```sh
gotestsum --watch -- -coverprofile=coverage.out
```

### benchmark

Run benchmarks.
Expand Down
142 changes: 139 additions & 3 deletions docs/docs/03-syntax-and-usage/12-script-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Scripts

Use standard `<script>` tags, and standard HTML attributes.
Use standard `<script>` tags, and standard HTML attributes to run JavaScript on the client.

```templ
templ body() {
Expand All @@ -17,7 +17,7 @@ templ body() {

## Importing scripts

You can also use standard `<script>` tags to load JavaScript from a URL.
Use standard `<script>` tags to load JavaScript from a URL.

```templ
templ head() {
Expand All @@ -27,7 +27,7 @@ templ head() {
}
```

You can then use the imported JavaScript directly in templ.
And use the imported JavaScript directly in templ via `<script>` tags.

```templ
templ body() {
Expand All @@ -50,8 +50,144 @@ templ body() {
}
```

:::tip
You can use a CDN to serve 3rd party scripts, or serve your own and 3rd party scripts from your server using a `http.FileServer`.

```go
mux := http.NewServeMux()
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))
http.ListenAndServe("localhost:8080", mux)
```
:::

## Passing server-side data to scripts

Pass data from the server to the client by embedding it in the HTML as a JSON object in an attribute or script tag.

### Pass server-side data to the client in a HTML attribute

```templ title="input.templ"
templ body(data any) {
<button id="alerter" alert-data={ templ.JSONString(attributeData) }>Show alert</button>
}
```

```html title="output.html"
<button id="alerter" alert-data="{&quot;msg&quot;:&quot;Hello, from the attribute data&quot;}">Show alert</button>
```

The data in the attribute can then be accessed from client-side JavaScript.

```javascript
const button = document.getElementById('alerter');
const data = JSON.parse(button.getAttribute('alert-data'));
```

### Pass server-side data to the client in a script element

```templ title="input.templ"
templ body(data any) {
@templ.JSONScript("id", data)
}
```

```html title="output.html"
<script id="id" type="application/json">{"msg":"Hello, from the script data"}</script>
```

The data in the script tag can then be accessed from client-side JavaScript.

```javascript
const data = JSON.parse(document.getElementById('id').textContent);
```

## Working with NPM projects

https://github.com/a-h/templ/tree/main/examples/typescript contains a TypeScript example that uses `esbuild` to transpile TypeScript into plain JavaScript, along with any required `npm` modules.

After transpilation and bundling, the output JavaScript code can be used in a web page by including a `<script>` tag.

### Creating a TypeScript project

Create a new TypeScript project with `npm`, and install TypeScript and `esbuild` as development dependencies.

```bash
mkdir ts
cd ts
npm init
npm install --save-dev typescript esbuild
```

Create a `src` directory to hold the TypeScript code.

```bash
mkdir src
```

And add a TypeScript file to the `src` directory.

```typescript title="ts/src/index.ts"
function hello() {
console.log('Hello, from TypeScript');
}
```

### Bundling TypeScript code

Add a script to build the TypeScript code in `index.ts` and copy it to an output directory (in this case `./assets/js/index.js`).

```json title="ts/package.json"
{
"name": "ts",
"version": "1.0.0",
"scripts": {
"build": "esbuild --bundle --minify --outfile=../assets/js/index.js ./src/index.ts"
},
"devDependencies": {
"esbuild": "0.21.3",
"typescript": "^5.4.5"
}
}
```

After running `npm build` in the `ts` directory, the TypeScript code is transpiled into JavaScript and copied to the output directory.

### Using the output JavaScript

The output file `../assets/js/index.js` can then be used in a templ project.

```templ title="components/head.templ"
templ head() {
<head>
<script src="/assets/js/index.js"></script>
</head>
}
```

You will need to configure your Go web server to serve the static content.

```go title="main.go"
func main() {
mux := http.NewServeMux()
// Serve the JS bundle.
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))

// Serve components.
data := map[string]any{"msg": "Hello, World!"}
h := templ.Handler(components.Page(data))
mux.Handle("/", h)

fmt.Println("Listening on http://localhost:8080")
http.ListenAndServe("localhost:8080", mux)
}
```

## Script templates

:::warning
Script templates are a legacy feature and are not recommended for new projects. Use standard `<script>` tags to import a standalone JavaScript file, optionally created by a bundler like `esbuild`.
:::

If you need to pass Go data to scripts, you can use a script template.

Here, the `page` HTML template includes a `script` element that loads a charting library, which is then used by the `body` element to render some data.
Expand Down
38 changes: 2 additions & 36 deletions examples/typescript/components/index.templ
Original file line number Diff line number Diff line change
@@ -1,43 +1,9 @@
package components

import (
"encoding/json"
"fmt"
)

type Data struct {
Message string `json:"msg"`
}

func JSON(v any) (string, error) {
s, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(s), nil
}

func JSONScript(id string, data any) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
dataJSON, err := json.Marshal(data)
if err != nil {
return err
}
if _, err = io.WriteString(w, `<script`); err != nil {
return err
}
if id != "" {
if _, err = fmt.Fprintf(w, ` id="%s"`, templ.EscapeString(id)); err != nil {
return err
}
}
if _, err = fmt.Fprintf(w, ` type="application/json">%s</script>`, string(dataJSON)); err != nil {
return err
}
return nil
})
}

templ Page(attributeData Data, scriptData Data) {
<!DOCTYPE html>
<html>
Expand All @@ -46,8 +12,8 @@ templ Page(attributeData Data, scriptData Data) {
<script src="/assets/js/index.js" defer></script>
</head>
<body>
<button id="attributeAlerter" alert-data={ JSON(attributeData) }>Show alert from data in alert-data attribute</button>
@JSONScript("scriptData", scriptData)
<button id="attributeAlerter" alert-data={ templ.JSONString(attributeData) }>Show alert from data in alert-data attribute</button>
@templ.JSONScript("scriptData", scriptData)
<button id="scriptAlerter">Show alert from data in script</button>
</body>
</html>
Expand Down
40 changes: 3 additions & 37 deletions examples/typescript/components/index_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@
pkgs.mkShell {
buildInputs = with pkgs; [
(golangci-lint.override { buildGoModule = buildGo121Module; })
cosign # Used to sign container images.
esbuild # Used to package JS examples.
go_1_21
gomod2nix.legacyPackages.${system}.gomod2nix
gopls
goreleaser
nodejs # Used to build templ-docs.
gotestsum
ko # Used to build Docker images.
cosign # Used to sign container images.
gomod2nix.legacyPackages.${system}.gomod2nix
nodejs # Used to build templ-docs.
xc.packages.${system}.xc
];
});
Expand Down
Loading
Loading