Components are individual files which are used to organize your user interface code. Each component lives in a .vugu file. Each .vugu file is processed to produce a .go file. Like all Go code, each directory is a package and you may add additional .go files to it and use them as part of your component. Vugu does some code generation, but otherwise does not interfere with the regular Go build process at all.

Under the hood, components implement the ComponentType interface. Although this is unimportant to most developers as the code generator does the work to implement it based on your .vugu file.

By default, the component named root (and thus living in root.vugu) is the top level component and is rendered just inside the <body> tag on your page. In this case, there is only one instance of your root component, which is created in your main() function. (If you've followed the Getting Started instructions you can find this code in main_wasm.go)

Things get interesting when we introduce the idea of multiple components into an application. Each component goes in it's own .vugu file. Use at least one hyphen to avoid name conflicts with regular HTML tags.

One component can include an HTML tag with the name of another component. When this happens, it indicates than an instance of this other component should be created. Let's look at an example:

  1. <!-- root.vugu -->
  2. <div class="root">
  3. <ul>
  4. <my-line file-name="example.txt" :line-number="rand.Int63n(100)">
  5. </ul>
  6. </div>
  7.  
  8. <script type="application/x-go">
  9. import "math/rand"
  10. </script>
  1. <!-- my-line.vugu -->
  2.  
  3. <li class="my-line">
  4. <strong vg-html='data.FileName'></strong>:<span vg-html='data.LineNumber'></span>
  5. </li>
  6.  
  7. <script type="application/x-go">
  8. type MyLineData struct {
  9. FileName string
  10. LineNumber int
  11. }
  12. func (comp *MyLine) NewData(props vugu.Props) (interface{}, error) {
  13. ret := &MyLineData{}
  14. ret.FileName, _ = props["file-name"].(string)
  15. ret.LineNumber, _ = props["line-number"].(int)
  16. return ret, nil
  17. }
  18. </script>

In this case the <my-line> tag gets replaced with the <li> tag and its contents as rendered by the my-line component.

As shown in the example, the HTML attributes of the tag are passed as properties (vugu.Props, which is just a map[string]interface{}) to the component instance. Attributes with a colon (:) will be evaluated as Go code and then the result passed as-is (not converted in any way). This allows you to pass arbitrarily complex data, or pointers, etc. into components if needed.

The NewData method on the component type is responsible for reading the props passed into it and populating the data struct for this component instance. (This method is part of satisfying the ComponentType interface.)

Components can be instantiated as many times as needed. Each one causes a new instance to be created. Like so:

  1. <!-- root.vugu -->
  2.  
  3. <div class="root">
  4. <ul>
  5. <my-line vg-for='i := 0; i < 10; i++'
  6. file-name="example.txt" :line-number="i">
  7. </li>
  8. </div>
  9.  
  10. <script type="application/x-go">
  11. import "math/rand"
  12. </script>

Vugu needs a way to determine, when re-rendering the HTML, which components are the same and which are now different and should be re-created. This is determined by calling ComputeHash on the attributes (including dynamically evaluted/colon prefixed ones after evaluation) and if the hash is different a new instance is created. If it's the same, the same one is re-used.

Any component can include any other registered component. But if components include each other in a loop the behavior is undefined (but I can promise you it won't be good).

Note

By default components register themselves in an init() function by calling RegisterComponentType. And by default in main() all registered components are retrieved and made available. So you can place import _ "your/package" in any file and if that package contains components they will be available for use. This behavior can also easily be changed by modifying the appropriate behavior in main_wasm.go, in case you need more control over what components are available under what names.