gomodest-template
gomodest-template copied to clipboard
A template to build dynamic web apps quickly using Go, html/template and javascript
gomodest-template
A modest template to build dynamic web apps in Go, HTML and sprinkles and spots of javascript.
Why ?
- Build dynamic websites using the tools you already know(Go, HTML, CSS, Vanilla Javascript) for the most part.
- Use bulma components to speed up prototyping a responsive and good-looking UI.
- Use turbo & stimulusjs for most of the interactivity.
- For really complex interactivity use Svelte for a single div in a few spots.
- Lightweight and productive. Fast development cycle.
- Easy to start, easy to maintain.
For a more complete implementation using this technique please see gomodest-starter.
Usage
- Use as a github template
-
git clone https://github.com/<user>/<mytemplate>
andcd /path/to/your/gomodest-template
-
make watch
(starts hot-reload for go, html and javascript changes) - open localhost:3000.
or
brew install gh
gh repo create myapp --template adnaan/gomodest-template
cd myapp
make install # or (make install-x64)
# replace gomodest-template with your app name
go get github.com/piranha/goreplace
$(go env GOPATH)/bin/goreplace gomodest-template -r myapp
git add . && git commit -m "replace gomodest-template"
make watch # or make watch-x64
TOC
- Folder Structure
-
Views using html templates
- Step 1: Add a layout partial
- Step 2: Add a layout
- Step 4: Add a view partial
- Step 5: Add a view
- Step 6: Render view
-
Interactivity using Javascript
-
Stimulus Controllers
- Step 1: Add a controller
- Step 2: Add data attributes to the target div
-
Svelte Components
- Step 1: Add data attributes to the target div.
- Step 2: Create and export svelte component
- Step 3: Hydrate initial props from the server
-
Stimulus Controllers
- Styling and Images
- Samples
- Dependencies
Table of contents generated with markdown-toc
Folder Structure
-
templates/
- layouts/
- partials/
- list of view files
-
assets/
-
images/
-
src/
- components/
- controllers/
- index.js
- styles.scss
-
-
templates
is the root directory where all html/templates assets are found. -
layouts
contains the layout files. Layout is a container forpartials
andview files
-
partials
contains the partial files. Partial is a reusable html template which can be used in one of two ways:-
Included in a
layout
file:{{include "partials/header"}}
-
Included in a
view
file:{{template "main" .}}
. When used in a view file, a partial must be enclosed in adefine
tag:{{define "main"}} Hello {{.hello}} {{end}}
-
-
view
files are put in the root of thetemplates
directory. They are contained within alayout
must be enclosed in adefine content
tag:{{define "content"}} App's {{.dashboard}} {{end}}
View
is rendered within alayout
:indexLayout, err := rl.New( rl.Layout("index"), rl.DisableCache(true), rl.DefaultHandler(func(w http.ResponseWriter, r *http.Request) (rl.M, error) { return rl.M{ "app_name": "gomdest-template", }, nil })) ... r.Get("/", indexLayout.Handle("home", func(w http.ResponseWriter, r *http.Request) (rl.M, error) { return rl.M{ "hello": "world", }, nil }))
Here the
view
:home
is rendered within theindex
layout.
Please see the templates
directory.
-
assets
directory contains the public asset pipeline for the project.-
styles.scss
is a customscss
file for [bulma][https://bulma.io] as documented here. -
index.js
is the entrypoint for loadingstimulusjs
controllers sourced from this [example](https://github.com/hotwired/stimulus-starter. -
controllers
contains stimulusjs controllers. -
components
contains single file svelte components.
-
Views using html templates
There are three kinds of html/template
files in this project.
-
layout
: defines the base structure of a web page. -
partial
: reusable snippets of html. It can be of two types:layout partials
&view partials
. -
view
: the main container for the web page logic contained within alayout
. It must be enclosed in adefine content
template definition. It can use view partials.
Step 1: Add a layout partial
Create header.html
file in templates/partials
.
<meta charset="UTF-8">
<meta name="description" content="A modest way to build golang web apps">
<meta name="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
...
Step 2: Add a layout
Create index.html
file in templates/layouts
and use the above partial.
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{.app_name}}</title>
{{include "partials/header"}}
</head>
<body ...>
...
</body>
</html>
Step 4: Add a view partial
Create main.html
in templates/partials
{{define "main"}}
<main>
<div class="columns is-centered is-vcentered is-mobile py-5">
<div class="column is-narrow" style="width: 70%">
<h1 class="has-text-centered title">Hello {{.hello}}</h1>
</div>
</div>
</main>
{{end}}
This is a different from the layout partial
since it's closed in a define
tag.
Step 5: Add a view
Create home.html
in templates
and use the above partial.
{{define "content"}}
<div class="columns is-mobile is-centered">
<div class="column is-half-desktop">
{{template "main" .}}
</div>
</div>
{{end}}
Notice that a view
is always enclosed in define content
template definition.
Step 6: Render view
To render the view with data we use a wrapper over the html/template
package.
r.Get("/", indexLayout.Handle("home",
func(w http.ResponseWriter, r *http.Request) (rl.M, error) {
return rl.M{
"hello": "world",
}, nil
}))
To learn more about html/template
, please look into this amazing cheatsheet.
Reference:
-
templates/layout/index.html
-
templates/partials/header.html
-
templates/partials/main.html
-
templates/home.html
-
main.go
Interactivity using Javascript
For client-side interactivity we use a bit of javascript.
Stimulus Controllers
A stimulus controller is a snippet of javascript which handles a single aspect of interactivity. To add a new svelte component:
Step 1: Add a controller
Create a file with suffix: _controller.js
util_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
...
connect(){
}
goto(e){
if (e.currentTarget.dataset.goto){
window.location = e.currentTarget.dataset.goto;
}
}
goback(e){
window.history.back();
}
...
}
See complete implementation in assets/src/controller/util_controller.js
. To understand how stimulus works, please see the handbook.
Step 2: Add data attributes to the target div
<body data-controller="util svelte"
data-action="keydown@window->util#keyDown "
data-util-active-class="is-active">
...
<button class="button"
data-action="click->util#goto"
data-goto="/">Home
</button>
</body>
Here we are attaching two controllers to the body
itself since they are used often. Later we can add action and data attributes to use them.
Reference:
-
templates/layout/index.html
-
templates/404.html
-
assets/src/controllers/util_controller.js
Svelte Components
A svelte component is loaded into the targeted div by a stimulujs controller: controllers/svelte_controller.js
. This is hooked by declaring data attributes on the div which is to be contain the svelte component:
-
data-svelte-target
: Value is required to becomponent
. It's used for identifying the divs as targets for thesvelte_controller
. -
data-component-name
: The name of the component as exported insrc/components/index.js
import app from "./App.svelte" // export other components here. export default { app: app, }
-
data-component-props
: A string map object which is passed as initial props to the svelte component.
To add a new svelte component:
Step 1: Add data attributes to the target div.
{{define "content"}}
<div class="columns is-mobile is-centered">
<div class="column is-half-desktop">
<div
data-svelte-target="component"
data-component-name="app"
data-component-props="{{.Data}}">
</div>
</div>
</div>
</div>
{{end}}
Step 2: Create and export svelte component
- Create a new svelte component in
src/components
and export it insrc/components/index.js
import app from "./App.svelte"
// export other components here.
export default {
app: app,
}
The controllers/svelte_controller.js
controller loads the svelte component in to the div with the required data attributes shown in step 1.
Step 3: Hydrate initial props from the server
It's possible to hydrate initial props from the server and pass onto the component. This is done by templating a string data object into the data-component-props
attribute.
r.Get("/app", indexLayout.Handle("app",
func(w http.ResponseWriter, r *http.Request) (rl.M, error) {
appData := struct {
Title string `json:"title"`
}{
Title: "Hello from server for the svelte component",
}
d, err := json.Marshal(&appData)
if err != nil {
return nil, fmt.Errorf("%v: %w", err, fmt.Errorf("encoding failed"))
}
return rl.M{
"Data": string(d), // notice struct data is converted into a string
}, nil
}))
Reference:
-
templates/app.html
-
src/controllers/svelte_controller.js
-
src/components/*
-
main.go
Styling and Images
Bulma is included by default. Bulma is a productive css framework with prebuilt components and helper utilities.
-
assets/src/styles.scss
: to override default bulma variables.webpack
bundles and copies css assets to `public/assets/css. -
assets/images
: put image assets here. it will be auto-copied topublic/assets/images
bywebpack
.
Samples
Go to localhost:3000/samples to a list of sample views. Copy-paste at will from the templates/samples
directory.