go export html report (using hero precompiled html template engine)

Posted by nepton on Wed, 27 Oct 2021 12:11:03 +0200

preface

According to the project requirements, the data of the server needs to be exported for easy portability and display. An html report is exported and how to implement it is considered.

  1. Use front-end frameworks such as vue to export the data into json files one by one, and then dynamically render the html view by reading js
  2. Directly write the data to the html page, export more html, and load the page through iframe

The second method is used here.

This article mainly introduces an open source library of go hero precompiled template engine For fast rendering of html.

Common template engines generally have two implementation methods: one is to directly parse the HTML syntax tree and then dynamically splice it according to certain rules; the other is to pre generate code from the template and call relevant functions when rendering the template.

go's official built-in template package is the first implementation, and the protagonist of this article uses the second.

Introduction to hero

You can go directly github Let's make a brief introduction here.

install

go get github.com/shiyanhui/hero
go get github.com/shiyanhui/hero/hero

// Hero needs goimports to handle the generated go code, so you need to install goimports
go get golang.org/x/tools/cmd/goimports

use

hero [options]

options:
	- source:     The template directory is the current directory by default
	- dest:       Generated go The directory of the code, if not set, and source equally
	- pkgname:    Generated go The name of the code package. The default is template
  - extensions: source File suffix, If there are more than one, separate them with English commas, Default to.html
	- watch:      Monitor template file changes and compile automatically

example:
	hero -source="./"
	hero -source="$GOPATH/src/app/template" -watch

Basic grammar

Hero has nine statements in total, which are:

  • Function definition statement <%: func define% >

    • This statement defines the function corresponding to the template. If there is no function definition statement in a template, the corresponding function will not be generated in the final result.
    • The last parameter of the function must be * bytes.Buffer or io. Writer. Hero will automatically recognize the name of the parameter and write the result to the parameter.
    • Example:
      • <%: func UserList(userList []string, buffer *bytes.Buffer) %>
      • <%: func UserList(userList []string, w io.Writer) %>
      • <%: func UserList(userList []string, w io.Writer) (int, error) %>
  • Template inheritance statement <% ~ "parent template"% >

    • This statement declares the template to inherit.
    • Example: <% ~ "index. HTML" >
  • Template include statement <% + "sub template"% >

    • This statement loads the template to be included into the template. The working principle is a little similar to that of #include in C + +.
    • Example: <% + "user. HTML" >
  • Package import statement <%! go code %>

    • This statement is used to declare all code outside the function, including dependent package import, global variables, const, etc.

    • The statement is not inherited by the child template

    • Example:

      <%!
      	import (
            	"fmt"
          	"strings"
          )
      
      	var a int
      
      	const b = "hello, world"
      
      	func Add(a, b int) int {
          	return a + b
      	}
      
      	type S struct {
          	Name string
      	}
      
      	func (s S) String() string {
          	return s.Name
      	}
      %>
      
  • Block statement <% @ blockname {% > <%}% >

    • Block statement is used to rewrite the block with the same name in the parent module in the child template, so as to realize the inheritance of the template.

    • Example:

      <!DOCTYPE html>
      <html>
          <head>
              <meta charset="utf-8">
          </head>
      
          <body>
              <%@ body { %>
              <% } %>
          </body>
      </html>
      
  • Go code statement <% go code% >

    • This statement defines the part of the code inside the function.

    • Example:

      <% for _, user := range userList { %>
          <% if user != "Alice" { %>
          	<%= user %>
          <% } %>
      <% } %>
      
      <%
      	a, b := 1, 2
      	c := Add(a, b)
      %>
      
  • Native value statement <% = = [t] variable% >

    • This statement converts a variable to a string.

    • T is the type of variable. hero will automatically select the conversion function according to t. The values to be selected for T are:

      • b: bool
      • i: int, int8, int16, int32, int64
      • u: byte, uint, uint8, uint16, uint32, uint64
      • f: float32, float64
      • s: string
      • bs: []byte
      • v: interface

      be careful:

      • If t is not set, t defaults to s
      • It is best not to use V, because its corresponding conversion function is fmt.Sprintf("%v", variable), which is very slow.
    • Example:

      <%== "hello" %>
      <%==i 34  %>
      <%==u Add(a, b) %>
      <%==s user.Name %>
      
  • Escape value statement <% = statement% >

    • This statement converts a variable into a string, and then it is escaped through html.escapes string.

    • T is the same as t in the native value statement above.

    • Example:

      <%= a %>
      <%= a + b %>
      <%= Add(a, b) %>
      <%= user.Name %>
      
  • Comment statement <%# note% >

    • This statement annotates the relevant template, and the comments will not be generated into the go code.
    • Example: < # this is a comment >

principle

The final generated code is written to io.Writer through string splicing. The following is an example. The generated code is as follows:

func WriteTreeNodeHtml(param *RenderTemplateParam, w io.Writer) {
	_buffer := hero.GetBuffer()
	defer hero.PutBuffer(_buffer)
	_buffer.WriteString(`

<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="css/build.css" />
        <link rel="stylesheet" href="css/jquery.treeview.css" />
        <link rel="stylesheet" href="css/screen.css" />

        <script src="js/jquery.min.js"></script>
        <script src="js/jquery.cookie.js"></script>
        <script src="js/jquery.treeview.js" type="text/javascript"></script>
        <script type="text/javascript">
        $(function() {
            $("#tree").treeview({
                collapsed: true,
                animated: "fast",
                control: "#sidetreecontrol",
                prerendered: true,
                persist: "location"
            });
        })
        </script>
    </head>

    <body style="margin: 10px;">
        <div>
            <h3>`)
	hero.EscapeHTML(GetAppName(), _buffer)
	_buffer.WriteString(`report</h3>
            <div id=jstree style="font-size:14px">
            <ul class="treeview" id="tree" style="margin-top:6px;">
                <li><a class="jstree-anchor" href="page1.html#case" target="pageframe">
                <i style="margin-left: 4px;margin-right: 4px;" class="icon-file iconfont"></i>case</a></li>
                <li><a class="jstree-anchor" href="page1.html#evidences" target="pageframe">
                <i style="margin-left: 4px;margin-right: 4px;" class="icon-evidence iconfont"></i>Inspection material information</a></li>
                <li><a class="jstree-anchor" href="page1.html#brief" target="pageframe">
                <i style="margin-left: 4px;margin-right: 4px;" class="icon-evidence iconfont"></i>Text summary of data statistics</a></li>
                <li><a class="jstree-anchor" href="page1.html#summary" target="pageframe">
                <i style="margin-left: 4px;margin-right: 4px;" class="icon-summary iconfont"></i>Data statistics list</a></li>
                `)
	treeNodes, ok := param.Data.([]*ReportTreeNode)
	if !ok {
		return
	}
	for _, node := range treeNodes {
		GenerateTreeNode(node, _buffer)
	}
	_buffer.WriteString(`
            </ul>
            </div>
        </div>
    </body>
</html>

`)
	w.Write(_buffer.Bytes())

}

summary

The principle of using go to generate html is very simple. You can write the data to the corresponding place through string splicing. The trouble is that the layout of html pages corresponds to the insertion of data.

reference resources

https://github.com/shiyanhui/hero

Topics: Go html