go language template engine, introduction to gtpl use

Posted by stevepatd on Fri, 18 Feb 2022 18:21:16 +0100

gtpl

Share a go language template engine written by yourself today. Convenient for template grammar analysis at very fast speed when the go language outputs HTML rendering. Compared with the go language official library html/template, the syntax of gtpl is concise, flexible and easy to use.

The ultimate goal of gtpl is to completely replace the go language's official, overly complex html/template rendering package, making template calls more flexible and understandable, saving developers a lot of time.

Unlike the php template engine

gtpl is completely different from those php-based template engines.

The PHP template engine translates the tag syntax directly into PHP code, saves it as a PHP file, reintroduces it from php, and runs directly. While gtpl is equivalent to a very lightweight programming language, it implements its own semantic analysis, so there are essential differences in the way it is handled. Recursive logic is required for the processing of each tag and context needs to be maintained during the recursion process. The author has done a lot of work for this.

At present, it still has some shortcomings, such as some unknown problems in the parsing of if statements that need to be solved, which I will focus on and fix in the next version.

gtpl can now use simple if conditional expressions, such as arithmetic and logical operations, which prioritize operators the same as go:

{!if id==1+2-(10*5/(11))}
    123
{;elseif id!=pid}
    {!if title=="hello"}
        ...
    {/if}
{;else}
    789
{/if}
However, two of the logical operators'&&'and'||' have not been processed yet. Therefore, you cannot use the following expression for the time being. It may return a clear error hint or output wrong data. In most cases, of course, the underlying layer can catch this exception:
// At the current version, this tag code will not run as expected
// this would not work in this version

{!if a==1&&b==2}
    something
{/if}
I'll focus on this in the next release.

The following are examples of tag calls commonly used by gtpl:

example a.go
import(
    "github.com/pywee/gtpl/handle/templates"
    "log"
    "net/http"
)

// data
// example struct
type Data struct {
    Id      int64
    WebTitle string
    Users   []*User
    Winner  *Demo
}

// example struct
type Demo struct {
	Str string
}

// example struct
type User struct {
	Id       int
	UserName string
	MyList   []*UserCustom
}

type UserCustom struct {
    PhoneNumber string
}

func main() {
    // create data that you want to render to HTML
    // Assembling data to render to HTML
    data := &Data{
        Id:    1,
        WebTitle: "WebTitle",
        Winner: &Demo{
            Str: "pywee",
        },
        Users: []*User{
            {
                Id:       1,
                UserName: "Jim",
                MyList: []*UserCustom{
                    {PhoneNumber: "1"},
                    {PhoneNumber: "2"},
                },
            },
            {
                Id:       2,
                UserName: "Lucy",
                MyList: []*UserCustom{
                    {PhoneNumber: "1"},
                    {PhoneNumber: "2"},
                },
            },
        },
    }

    // http server example
	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {

		// Instantiation gtpl
		p := templates.NewParser()

		// Introduce a template file to match data data data to calls in the template
		re, err := p.ParseFile("example/index.html", data)
		if err != nil {
			panic(err)
		}

		// print string
		// fmt.Println(re.String())

		// Output to screen, which acts as a template. Excute (w,'%s', data)
		re.Fprint(w)
	})
}

For the above code, you can get data in the html file in this way to get data in the html file (For the above code, you can get data in the html file in this way).

example a.html:

<html>
....

// Here you can access the fields directly under the data structure 
// (You can call the fields under the structure in this way)

// Access the data in the structure. Id, Data. When Title is called in html, note that lower case is used and underscores are used instead of the hump field in the structure
// to call Id and WebTitle of struct Data
{:id}
{:web_title}

// Access Other in the Data structure. Demo. Str
// to call Other.Demo.Str of struct Data
{:winner.str} 

// Execute built-in functions while accessing fields
{:trim(replace(title, "i am", "xx", -1), "xx")}

// For list processing, such as a users array under circular data, use the following
{:id}
{:winner.str}  // Access the Winner structure under Data, then the Str field under that structure
{!list:users}
    {!if id==(1*1+1-1-1)}
        man: {:user_name}
    {;elseif 1==21}
        women: {:user_name}
    {;else}
        {!list:my_list}
            {:phone_number}
        {/list}
    {/if}
{/list}
</html>

For the above label code, {list:users} can also be explicitly written as {list:data.users}, but the underlying processing is slightly different.
Most of the calls declared in the tag are recursively searched and matched over and over like stripping garlic. Therefore, you should use {list:users} whenever possible, even if this is a recursive way to handle grammar-related work, but by contrast, it will reduce the number of recursions.
(You can also use {!list:data.users}to ergodic slice users, but it is not good for performance.)

* Also, it is important to note that for efficiency reasons, gtpl does not currently support returning tag s of structs to obtain data for struct-specified fields, so when you call fields in structs in html, you need to underline''. The fields in the structure are still written in camel style.
If you call UserName in the structure, you need to use {: user_name} in the html file to call it.

gtpl supports calls to built-in functions in some go languages:

len()
tolower()
toupper()
replace()
trim()
ltrim()
rtrim()
trimspace()

// example
{!list:data}
    {:trim(title, "hello")}
{/list}

In addition, if the number of ParseFile functions passed in is an array structure, then you must first loop through it in html, for example:

example
    d2 := []*Data{ // slice
		{
			Id: 1,
			Users: []*User{
				{
					Id:       2,
					UserName: "title",
					MyList: []*UserCustom{
						{PhoneNumber: "1"},
						{PhoneNumber: "2"},
						{PhoneNumber: "3"},
					},
				},
			},
		},
	}

	p := templates.NewParser()
	re, err := p.ParseFile("example/index.html", d2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(re.String())
// Correct call method (right)
{:id}
{!list:data}
    {!list:users}
        {!list:my_list}
            {:a}
        {/list}
    {/list}
{/list}

// Correct call method (right)
{!list:users}
    {!if id==(1*1+1-1-1)}
        man: {:user_name}
    {;elseif id+1==21}
        women: {:user_name}
    {;else}
        {!list:my_list}
            {:phone_number}
        {/list}
    {/if}
{/list}

// Wrong invocation (wrong)
{!list:users}
    {!list:my_list}
        {:a}
    {/list}
{/list}

More functions or custom function calls will be supported in the future:

Portal https://github.com/pywee/gtpl









Topics: Go templates