directory

  • template
    • The template
    • Actions
      • annotation
      • Cut the blank space
      • Text output
      • Conditional statements
      • Looping statements
      • define
      • template
      • block
      • with
    • parameter
    • variable
    • function
      • and
      • or
      • call
      • html
      • js
      • index
      • len
      • not
      • print
      • printf
      • println
      • urlquery
      • The comparison function
    • Nested template
    • conclusion

Official definition:

Package template implements data-driven templates for generating textual output.

The template package is a data-driven text output template that essentially populates the written template with data.

The template

What is a template?

Here is a simple template example:

// Template definition
tepl := "My name is {{ . }}"

// Parse the template
tmpl, err := template.New("test").Parse(tepl)

// Data-driven templates
data := "jack"
err = tmpl.Execute(os.Stdout, data)
Copy the code

The period in the middle of {{and}} represents the data passed into the template, rendering different content based on the data passed in.

. Can represent any type in the GO language, such as struct, hash, etc.

The contents of {{and}} packages are collectively referred to as actions and are divided into two types:

  • Data Evaluation
  • Control structures

The result of the action evaluation is copied directly into the template, and the control structure is similar to that of the Go program. It is also conditional statements, loop statements, variables, function calls, etc.

Once the template has been successfully parsed, it can be safely used in a concurrent environment, where the output to the same io.writer may overlap (because the order of concurrent execution cannot be guaranteed).

Actions

There aren’t many actions in the template, so let’s look at them one by one.

annotation

{{/* comment */}}
Copy the code

Cut the blank space

// Clipping space before content {{-content -}} // Clipping space before content {{-content}}Copy the code

Text output

{{ pipeline }}
Copy the code

The data represented by pipeline produces output similar to that produced by calling the FMT.Print function, such that integer type 3 is converted to string “3” output.

Conditional statements

{{ if pipeline }} T1 {{ end }}

{{ if pipeline }} T1 {{ else }} T0 {{ end }}

{{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}

// The syntax above is a shorthand for the following
{{ if pipeline }} T1 {{ else}} {{if pipeline }} T0 { {end }}{{ end }}

{{ if pipeline }} T1 {{ else if pipeline }} T2 {{ else }} T0 {{ end }}
Copy the code

If the pipeline value is empty, T1 will not be printed, otherwise T1 will be printed.

Null values are false, 0, any nil pointer, interface value, array, slice, dictionary, and empty string “” (string of length 0).

Looping statements

{{ range pipeline }} T1 {{ end }}

If pipeline length is 0, output else
{{ range pipeline }} T1 {{ else }} T0 {{ end }}

// Get the container's subscript
{{ range $index, $value := pipeline }} T1 {{ end }}
Copy the code

The value of a pipeline must be one of array, slice, dictionary, or channel, iterating a value of type, printing multiple T1s based on the length of the value.

define

{{ define "name" }} T {{ end }}
Copy the code

Define a template named name.

template

{{ template "name" }}

{{ template "name" pipeline }}
Copy the code

Reference the template named name.

block

{{ block "name" pipeline }} T1 {{ end }}
Copy the code

The semantics of a block are that if there is a template named name, it is referenced and executed, and if there is no template named name, it is executed.

This is an extra step to determine whether a template exists and render different content based on this result.

with

{{ with pipeline }} T1 {{ end }}

// Output T0 if pipeline is null
{{ with pipeline }} T1 {{ else }} T0 {{ end }}

{{ with arg }}
    . // In this case. Is arg
{{ end }}
Copy the code

With creates a new context in which. Has nothing to do.

parameter

The value of a parameter can take many forms and can be evaluated to any type, including functions, Pointers (Pointers are automatically evaluated to the original value indirectly) :

  • Booleans, strings, characters, floating-point numbers, and complex numbers behave like Go
  • The keywordnilStands for gonil
  • The character period. Represents the result of a value
  • Variables that begin with the $character are their values
  • The fields of the structure are represented as.FieldThe result is the value of Field, which supports chained calls.Field1.Field2
  • The dictionary key is represented by.KeyThe result is the value of Key
  • Invoking The Method.Method The result is The value of invoking The Method with dot as The receiver
    • Methods either have one return value of any type or the second return value is error, no more, and if error is not nil, it just says an error and stops the template rendering
    • The result of a method call can continue the chain call.Field1.Key1.Method1.Field2.Key2.Method2
    • A set of declared variable methods can also be called$x.Method1.Field
    • Group calls with parenthesesprint (.Func1 arg1) (.Func2 arg2)(.StructValuedMethod "arg").Field

Perhaps the most difficult thing to understand here is the way functions are called. If you access a function in a structure’s method set, how does it behave differently from a function in a field?

Write a demo to test it:

type T struct {
	Add func(int) int
}

func (t *T) Sub(i int) int {
	log.Println("get argument i:", i)
	return i - 1
}

func arguments(a) {
	ts := &T{
		Add: func(i int) int {
			return i + 1
		},
	}
	tpl := Call method func Sub: {{.ts.Sub. Y}} '// Call method func Sub: {{.ts
	t, _ := template.New("test").Parse(tpl)
	t.Execute(os.Stdout, map[string]interface{} {"y": 3."ts": ts,
	})
}

output:

call field func Add4:call method func Sub: 2
Copy the code

You can conclude that if a function is a function field in a structure, the function is not called automatically and can only be called using the built-in call function.

If the function is a method in the structure’s method set, the method is automatically called and the return value is assigned to. If the function returns a new structure, map, the chain call can continue.

variable

A pipeline in an action can initialize variables to store results, and the syntax is simple:

$variable = pipeline
Copy the code

At this point, the action declares a variable without producing any output.

The range loop can declare two variables:

range $index, $element := pipeline
Copy the code

In if, with, and range, variables are scoped to where {{end}} is.

If it is not a control structure, the scope of declared variables extends to the entire template.

For example, declare variables at the beginning of a template:

{{ $pages := .pagination.Pages }}
{{ $current := .pagination.Current }}
Copy the code

At the start of rendering, the $variable is replaced with a value starting with. For example, $pages is replaced with.pagenation.Pages. So cross-references between templates do not pass variables, which are only used in a particular scope.

function

Templates look for functions in two places when rendering:

  • Custom function map
  • Global functions map, which are built into the template

Custom functions are registered using func (t *Template) Funcs(funcMap funcMap) *Template.

List of global functions:

and

Returns the result of the and Boolean operation between arguments, which is essentially the JavaScript logic operator &&, and returns the first value that can be converted to false, zero in Go, and the last value if both are true.

tpl := "{{ and .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{} {"x": 1."y": 0."z": 3,
})

output:

0
Copy the code

or

Logical operators | |, return the first can be turned into the value of the true value is zero, in the Go if all return to the last value is false.

tpl := "{{ or .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{} {"x": 1."y": 0."z": 3,
})

output:

1
Copy the code

call

Returns the result of calling the first function argument. The function must have one or two return values (the second return value must be error, template rendering will stop if the value is not nil)

tpl := "call: {{ call .x .y .z }} \n"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{} {"x": func(x, y int) int { return x+y},
    "y": 2."z": 3,
})

output:

5
Copy the code

html

Returns an escaped HTML string. This function cannot be used in HTML /template.

js

Returns the escaped JavaScript string.

index

Used when the first argument is array, slice, or map, returns the value of the corresponding subscript.

Index x 1 2 3 is equal to x[1][2][3].

len

Returns the length of the compound type.

not

Returns the opposite value of a Boolean type parameter.

print

Is equal to the FMT. Sprint.

printf

Is equal to the FMT. Sprintf.

println

Is equal to the FMT. Sprintln.

urlquery

Url Query escapes for strings and cannot be used in HTML /template packages.

// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
	return url.QueryEscape(evalArgs(args))
}
Copy the code

As you can see from the source code, this function directly calls url.queryescape to escape the string, and there’s nothing mysterious about it.

The comparison function

  • eq: = =
  • ge: > =
  • gt: >
  • le: < =
  • lt: <
  • ne: !=

Analyze two source code:

// eq evaluates the comparison a == b || a == c || ...
func eq(arg1 reflect.Value, arg2 ... reflect.Value) (bool, error) {
	v1 := indirectInterface(arg1)
	k1, err := basicKind(v1)
	iferr ! =nil {
		return false, err
	}
	if len(arg2) == 0 {
		return false, errNoComparison
	}
	for _, arg := range arg2 {
		v2 := indirectInterface(arg)
		k2, err := basicKind(v2)
		iferr ! =nil {
			return false, err
		}
		truth := false
		ifk1 ! = k2 {// Special case: Can compare integer values regardless of type's sign.
			switch {
			case k1 == intKind && k2 == uintKind:
				truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
			case k1 == uintKind && k2 == intKind:
				truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
			default:
				return false, errBadComparison
			}
		} else {
			switch k1 {
			case boolKind:
				truth = v1.Bool() == v2.Bool()
			case complexKind:
				truth = v1.Complex() == v2.Complex()
			case floatKind:
				truth = v1.Float() == v2.Float()
			case intKind:
				truth = v1.Int() == v2.Int()
			case stringKind:
				truth = v1.String() == v2.String()
			case uintKind:
				truth = v1.Uint() == v2.Uint()
			default:
				panic("invalid kind")}}if truth {
			return true.nil}}return false.nil
}

// ne evaluates the comparison a ! = b.
func ne(arg1, arg2 reflect.Value) (bool, error) {
	/ /! = is the inverse of ==.
	equal, err := eq(arg1, arg2)
	return! equal, err }Copy the code

Eq determines whether the interface types are equal first, and then whether the values are equal, nothing special.

Ne is simply a call to eq and then invert.

Ge, GT, LE, and LT are similar to eq, judging the type first and then the size.

Nested template

Here is a more complex example:

// Load the template
template.ParseFiles("templates/")

// Load multiple templates into a namespace (modules in the same namespace can reference each other)
template.ParseFiles("header.tmpl"."content.tmpl"."footer.tmpl")

// Must load failure panic
tmpl := template.Must(template.ParseFiles("layout.html"))

// Execute the loaded template file, the first one by default
tmpl.Execute(w, "test")

// If there are multiple templates in TMPL, you can specify the name of the template to execute
tmpl.ExecuteTemplate(w, "layout"."Hello world")
Copy the code

The name specified by ExecuteTemplate is the name defined “Name” in the template file.

conclusion

Instance of the Template type initialized by the Parse family of functions.

The Execute series of functions pass the data to the template to render the final string.

Templates are essentially a Parse function that loads multiple files into a Tempalte instance, parses the define keyword in the file to register named templates, references each other with template, and executes the corresponding data rendering.