Vugu Files: Markup (HTML/Go)

The Markup section is an element which has the HTML that is displayed for this file (each file is a "component", following the parlance of other UI libraries).

In addition to regular HTML, some specific attributes have special meaning in Vugu and allow you to introduce logic into your component's display.

Note that HTML namespaces like svg and math should work as expected.

Conditionals with vg-if

You can choose a condition for an element to be displayed using vg-if='condition'. The condition is regular Go code and during code generation is put directly between if and { in the resulting .go file. While the expression itself can be any valid Go code for an if statement, a common case is to reference a member variable of your Component struct. For example:

<div>
    <p vg-if='c.ShowText'>
        Conditional text here.
    </p>
</div>

<script type="application/x-go">
type Root struct { // component for "root"
    ShowText bool `vugu:"data"`
}
</script>

Note the use of c as the name of the method receiver (i.e. c is "the component I am in").

Loops with vg-for

Loops correspond directly to for blocks in Go code. All forms of loops are supported including the init; condition; post syntax as well as range expressions. For example:

<div>
    <p vg-for='i := 0; i < 10; i++'>
        <div vg-content="i"></div>
    </p>
</div>
<div>
    <p vg-for='_, item := range c.Items'>
        <div vg-content="item"></div>
    </p>
</div>

<script type="application/x-go">
type Root struct {
    Items []string `vugu:"data"`
}
</script>

Note that the vg-content attribute outputs the value as HTML inside the element. See below.

As a special case and for convenience, if the expression contains no whitespace it will be expanded to for key, value := range expr {. Example:

<div>
    <div vg-for='c.Items'>
        <div>
            Key: <span vg-content="key"></span>
            Value: <span vg-content="value"></span>
        </div>
    </div>
</div>

<script type="application/x-go">
type Root struct {
    Items []string `vugu:"data"`
}
</script>

By default, loop variables are shadowed (redeclared with the same name) inside the loop. This is done for convenience as the common case is you want references to these variables to not change if used inside DOM event handlers or other code. To disable loop variable shadowing, you can use vg-for.noshadow instead of vg-for.

When vg-for is used in conjuction with nested components, a vg-key attribute can be used to determine which components to re-use across render cycles. When vg-key appears on a vg-for it specifies the default expression for component references inside the loop. This can then be overridden on individual component references as needed.

HTML content with vg-content

The vg-content attribute is used to output an expression to the contents of an element. It corresponds to the innerHTML property. The attribute vg-html is a synonym for vg-content and has the exact same behavior.

The expression you provided is converted to a string and represented as HTML according to the rules described in SetInnerHTML. Generally speaking, the value will be converted to text and HTML escaped (both for security against XSS attacks, and for convenience). If you wish to override the HTML escaping and provide raw unescaped HTML, you need only provide a value that implements the HTMLer interface. This is easily done by casting to vugu.HTML.

<div>
    <p vg-content='"this content <will be> escaped"'>
    <p vg-content='vugu.HTML("this will output directly as <em>raw HTML</em>, no escaping")'>
    <p vg-content='123'> <!-- number will be output according to fmt.Sprintf %v rules -->
</div>

You may use variable names declared in earlier constructs (such as key or value from a for/range loop). Regular Go variable scoping rules apply, where each nested DOM element is equivalent to a Go { code block }.

Dynamic Attributes with : and vg-attr

The values of HTML attributes can be made dynamic and accept Go expressions. Dynamically changing attribute values has many uses such as applying CSS styles with the class attribute.

<div>
    <p :style='"background:"+c.BgColor'></p>
</div>

<script type="application/x-go">
type Root struct {
    BgColor string // e.g. "blue"
}
</script>

Note that in addition to the above use, dynamic attributes are frequently used in conjuction with components, where attributes that start with an uppercase letter become struct field assignments. In this case, the attributes are not converted to strings but are kept as regular Go expressions emitted directly into the generated code. See the Components page for more info.

For HTML elements (as opposed to components), the following rules apply to dynamic attribute values:

  • string values are placed as-is into the attribute's value
  • numbers (built-in types beginning with int, uint, float) are converted to a string using the applicable function in strconv
  • bool values that are true result in the attribute being included in the output, false will omit the attribute (e.g. <video :autoplay='someBool'>)
  • pointers which are not nil are followed and the rule for the type pointed to applied
  • nil pointer or interface values result in omitting the attribute from the output
  • fmt.Stringer values (with a String() string method) which are not nil are converted to a string by calling that method
  • Any other possible value does not have well-defined behavior and should be avoided. The current implementation will convert to a string per the fmt package.
  • The above is implemented by AddAttrInterface

You can also provide a value which implements VGAttributeLister and specify that it should be called with vg-attr=. Example:

<div>
  <p vg-attr='vugu.VGAttributeListerFunc(c.makeAttrs)'></p>
</div>

<script type="application/x-go">
type Root struct {
  BgColor string // e.g. "blue"
}
func (c *Root) makeAttrs() (ret []vugu.VGAttribute) {
  ret = append(ret, vugu.VGAttribute{
    Key:"style",
    Val:"background:"+c.BgColor,
  })
  return
}
</script>

:= is shorthand for vg-attr, e.g. <p :='vugu.VGAttributeListerFunc(c.makeAttrs)'> is also valid with the same behavior.

Template Elements with <vg-template>

In some situations it is useful to attach a condition or a loop to to an element without emitting a corresponding tag into the final output. For these cases you can use the vg-template tag. You can put vg-for and vg-if attributes on it which have the usual meaning but only its contents, not the tag itself, are included in the final output. Example:

<ul>
  <li>An item here</li>
  <li>Another item here</li>
  <vg-template vg-if="c.ShowExtra">
    <li>A conditional item here</li>
    <li>And another one</li>
    <li>And something else too</li>
  </vg-template>
</ul>

Property Assignment with .

Completely separate from HTML attributes, HTML DOM elements have JavaScript properties associated with them. These can be assigned from a Go expression by using a property name that starts with a period. Example:

<div>
    <input type="checkbox" .checked="false"/>
</div>

Values are evaluated as a Go expression (false in this case) and then run through json.Marshal and the resulting value is assigned in JS.

This can be useful for form or other elements that have JS properties which do not have an exact corresponding HTML attribute.

DOM Events with @

Event handlers can be attached to HTML elements (à la addEventListener) using special attributes prefixed with an @ symbol.

The attribute name after the @ indicates the event name, e.g. "click". The attribute value may be a Go function/method call or other valid Go statement.

You may use variable names declared in earlier constructs (such as key or value from a for/range loop). Regular Go variable scoping rules apply, where each nested DOM element is equivalent to a Go { code block }. The special variable event, of type vugu.DOMEvent is declared in this scope and available to access the context of the event. See DOM Events for more info. Example:

<div>
    <div vg-if='c.Show'>I am here!</div>
    <button @click='c.Toggle(event)'>Toggle me Silly</button>
</div>

<script type="application/x-go">
import "log"
func (c *Root) Toggle(e vugu.DOMEvent) {
    c.Show = !c.Show
    log.Printf("Toggled! Show is now %t", c.Show)
}
type Root struct {
    Show bool `vugu:"data"`
}
</script>

You can also place statements directly in the event attribute value. For example @click='c.Show=!c.Show' is valid.

DOM Element Access with vg-js-create and vg-js-populate

In some situations direct access to DOM elements via js.Value is needed. Two callbacks are provided for this: vg-js-create is called when the DOM element is created but before any children are populated. And vg-js-populate is called after all child elements exist. The DOM reference is passed in a variable named value and can then be used to handle the DOM node directly. Note that strictly speaking the value provided is only guaranteed to be valid until the next render pass starts. Depending on various factors, subsequent renders may or may not return the same value. Example:

<div>
  <canvas width="300" height="100"
    vg-js-populate="c.canvasPopulate(value)"
    ></canvas>
</div>

<script type="application/x-go">
type Root struct {}
func (c *Root) canvasPopulate(value js.Value) {
    drawCtx := value.Call("getContext", "2d")
    drawCtx.Set("font", "30px Arial")
    drawCtx.Call("strokeText", "Hello from Vugu!", 10, 50)
}
</script>

Special Variable Names

Several variable names have special meaning and are useful when writing .vugu files:

  • c - Refers to the instance of your Component. It will be a struct pointer. This is the proper place to house the state of your component. By default the type of c is a named but empty struct. However, it is common to create your own struct with the data you need on it. See more at Components
  • event - This is the variable name for a vugu.DOMEvent instance that is created when a DOM event is triggered and your handler is called. This also provides some other needed functionality such as the EventEnv, which is important for synchronizing goroutines that need to update data after an event completes. See more at DOM Events.
  • value is used as the variable name in vg-js-create and vg-js-populate. See above.
  • key and value - See section covering vg-for above. These are the default names used for implied range expressions.

Please note that variables that your code declares, e.g. in a vg-for loop, should not start with vg, in order to avoid conflicting with generated code.

Tag and Attribute Naming Conventions

The following is a general list of rules which apply as regards the names of tags and attributes in Vugu markup:

  • Attributes (with no prefix character) are just strings. Given <div id="blah">, "blah" is just a string (no quotes included).
  • Attribute names preceded with a colon are Go expressions. Given <div :id='"bl"+"ah"'>`, "bl"+"ah" is a Go expression.
  • Attributes beginning with vg- contain Go code, according to their named purpose. E.g. vg-content is used for an expression that converted and used as the contents of an element, vg-for contains the loop of a for statement, and vg-if contains the condition of an if statement.
  • Tags that start with vg- can have specialized attributes which are not prefixed (e.g. <vg-comp expr="....)
  • Static component references contain a colon in the element name in the form of pkg:Component. E.g. <somepkg:SomeComp> corresponds to a struct named SomeComp in a package imported as "somepkg".
  • Attribute names that start with an upper case letter correspond to Go struct field names. It seems a happy coincidence that modern HTML documents almost always do (and always can) use lower case attribute names. This leaves attribute names that start with an upper case letter to have the meaning of corresponding to a (exported) Go struct field name. E.g. given <somepkg:SomeComp :FieldA="1" FieldB="blah">, FieldA and FieldB are struct fields on SomeComp, but notice the colon and so these correspond to someCompInst.FieldA = 1 and someCompInst.FieldB = "blah"
  • Lower case attributes on components are dynamic

Full-HTML Mode

Your root.vugu can now start with an <html> tag.

This allows access to title and meta tags as well as style and JS script includes.

Certain aspects of full-HTML mode are still under construction. Please create an issue on GitHub if you run into something missing that you need.

A few rules that apply when using full-HTML mode:

  • Only the root component in the application can start with an <html> tag.
  • Style and script tags have their sequence preserved but may be output at a different location on the page than where you put them. (This is for reasons having to do with how Vugu accumulates and renders style and script elements in various components and how it copes with the disparity between full-HTML and regular components.)
  • The body tag must contain a single element and this is used as the main content of the component during rendering. Attempting to include multiple elements directly inside <body> may lead to unspecified behavior and/or errors.