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.
Tip
Component files are parsed first using a regular HTML parser, before any Go expressions are considered.
As such, using single quotes when writing attribute values can help with HTML escaping issues.
For example, you can write <div vg-if='myvar == "myval"'>
or
<div vg-if="myvar == "myval"">
, but the former is easier to read.
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 {
<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-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 ofc
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 Componentsevent
- 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
andvalue
- 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, andvg-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 namedSomeComp
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
andFieldB
are struct fields onSomeComp
, but notice the colon and so these correspond tosomeCompInst.FieldA = 1
andsomeCompInst.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.