Go code can be included in your component with a
<script type="application/x-go">
Tip: Script Tag Alternative
Because Go packages impose few limits on which code may be put into which file,
you are not required to use a <script type="application/x-go">
A component corresponds to a single, exported struct in Go. By default, the name
of the file is converted from snake case to camel case. For example some-thing.vugu
corresponds to a type SomeThing struct
.
If you do not define this struct in the package, the code generator will output its definition as an empty struct.
In writing Go code blocks for your components, it is important to observe the following naming conventions:
- component-name.vugu results in a code generated file called component-name_vgen.go in the same directory
- component-name.vugu implies a struct called ComponentName which corresponds to the type of the component.
- ComponentName can and should have data and methods needed by the component.
Let's take a look at a working example of a component that makes more use of the Go code section and see all of this work together. This example shows a button which, when clicked, will cause the browser to fetch data from the CoinDesk Bitcoin Price Index API, parse the result, and display it.
root.vugu:
<div class="demo"> <div vg-if='c.IsLoading'>Loading...</div> <div vg-if='len(c.PriceData.BPI) > 0'> <div>Updated: <span vg-content='c.PriceData.Time.Updated'></span></div> <ul> <li vg-for='c.PriceData.BPI' vg-key='key'> <span vg-content='key'></span> <span vg-content='fmt.Sprint(value.Symbol, value.RateFloat)'></span> </li> </ul> </div> <button @click="c.HandleClick(event)">Fetch Bitcoin Price Index</button> </div> <script type="application/x-go"> import "encoding/json" import "net/http" import "log" type Root struct { PriceData bpi `vugu:"data"` IsLoading bool `vugu:"data"` } type bpi struct { Time struct { Updated string `json:"updated"` } `json:"time"` BPI map[string]struct { Code string `json:"code"` Symbol string `json:"symbol"` RateFloat float64 `json:"rate_float"` } `json:"bpi"` } func (c *Root) HandleClick(event vugu.DOMEvent) { c.PriceData = bpi{} ee := event.EventEnv() go func() { ee.Lock() c.IsLoading = true ee.UnlockRender() res, err := http.Get("https://api.coindesk.com/v1/bpi/currentprice.json") if err != nil { log.Printf("Error fetch()ing: %v", err) return } defer res.Body.Close() var newb bpi err = json.NewDecoder(res.Body).Decode(&newb) if err != nil { log.Printf("Error JSON decoding: %v", err) return } ee.Lock() defer ee.UnlockRender() c.PriceData = newb c.IsLoading = false }() } </script>
- Since this example lives in root.vugu, the corresponding struct is Root. (And for example in a component in file sample-comp.vugu, this would be SampleComp.) This is where a component's instance data is stored.
-
On Root we have a variable called IsLoading of type
bool
. This provides a simple way tovg-if
a div, which then shows only during loading. (More below on howIsLoading
is updated.) The`vugu:"data"`
struct tag indicates that this variable should be examined when determining if this component should be re-rendered. (See Modification Tracking for more info.) -
A struct
bpi
is defined for the data we're getting from the API call. This makes it easy to read and display. Remember, components are converted to regular Go code, and heeding the fact that your code runs in the browser, many of the usual approaches you would do in any Go program apply here. -
We're looping over one of the members of
bpi
usingvg-for
in our markup to display each individual item returned. -
A handler for the DOM click event is enabled with
@click
. Notice that the method it calls is a member ofc
(of type*Root
). This call does not have to be to a methd onc
, but this is the most common case. - When the click handler is invoked, we start a goroutine to fetch data from the server in the background. It is important we don't block here and return from this method quickly.
-
The EventEnv is used to synchronize access to data, and works well from goroutines. When we modify
any information on
c
or that could potentially have concurrency issues, you can useEventEnv.Lock()
to get an exclusive lock, andEventEnv.UnlockRender()
to release the lock and tell render loop that the page needs to be updated. See DOM Events for more info. As of this writing, goroutines do not execute concurrently in WebAssembly, but nonetheless we want to follow best practices regarding locking. The idiom of acquiring aLock
and then callingUnlockRender
when done combines proper locking (and future-proofs our code for when parallel execution is available in WebAssembly) with a clear indication of when re-rendering should occur.
github.com/vugu/vugu/js
In order to ease the development of server-side page rendering along side client-side Vugu page rendering, the package
github.com/vugu/vugu/js exists and is a lightweight
wrapper around Go's syscall/js
package, but with the distinct difference than it will compile on non-wasm
platforms and all operations will return appropriate zero or error values (or in some cases panic).
The point is that you don't have to separate out your files with a build tag every time you need to declare a
variable of type js.Value
.
All code generated Vugu code uses github.com/vugu/vugu/js
instead of syscall/js
and you are encouraged to do this as well. It's very lightweight and there are no known bugs.
Import Deduplication
Go code emitted from vugugen will have its import statements deduplicated.