diff --git a/examples/text-templates/text-templates.go b/examples/text-templates/text-templates.go index cd0d9a0..ec391fd 100644 --- a/examples/text-templates/text-templates.go +++ b/examples/text-templates/text-templates.go @@ -1,38 +1,36 @@ -// Go offers built-in support for creating -// dynamic content or showing customized output to the user called Template. +// Go offers built-in support for creating dynamic content or showing customized +// output to the user with the `text/template` package. A sibling package +// named `html/template` provides the same API but has additional security +// features and should be used for generating HTML. package main -// Go has two template packages. one is "text/template" for -// regular text manipulation, and another one is "html/template" -// which has the same API as "text/template" but has additional security features. -// It should be used when generating HTML. import ( - "log" "os" "text/template" ) func main() { - // New creates a template with a specific name and returns a pointer to it. + // We can create a new template and parse its body from + // a string. + // Templates are a mix of static text and "actions" enclosed in + // `{{...}}` that are used to dynamically insert content. t1 := template.New("t1") - - // Parse parses its parameter as template body. - // We use {{.}} to access the value passed to the template when it's getting executed. t1, err := t1.Parse("Value is {{.}}\n") if err != nil { - log.Fatal(err) + panic(err) } - // If we want to ignore the errors we can use Must function. - // It will panic if an error occurs when parsing the template. - t1 = template.Must(t1.Parse("Value is {{.}}\n")) + // Alternatively, we can use the `template.Must` function to + // panic in case `Parse` returns an error. This is especially + // useful for templates initialized in the global scope. + t1 = template.Must(t1.Parse("Value: {{.}}\n")) - // Execute applies parsed template to the data we pass to it and writes the output to the io.Writer. - t1.Execute(os.Stdout, t1.Name()) + // By "executing" the template we generate its text with + // specific values for its actions. The `{{.}}` action is + // replaced by the value passed as a parameter to `Execute`. t1.Execute(os.Stdout, "some text") - t1.Execute(os.Stdout, true) t1.Execute(os.Stdout, 5) t1.Execute(os.Stdout, []string{ "Go", @@ -40,61 +38,41 @@ func main() { "C++", "C#", }) - t1.Execute(os.Stdout, struct{ name string }{ - name: "Jane Doe", - }) - // If the data is a struct we can use the {{.FieldName}} action to access its fields. - // The fields should be exported to be accessible when template is executing. - t2, _ := template. - New("t2"). - Parse("Fullname: {{.Fullname}}\n") + // Helper function we'll use below. + Create := func(name, t string) *template.Template { + return template.Must(template.New(name).Parse(t)) + } + + // If the data is a struct we can use the `{{.FieldName}}` action to access + // its fields. The fields should be exported to be accessible when a + // template is executing. + t2 := Create("t2", "Name: {{.Name}}\n") t2.Execute(os.Stdout, struct { - Fullname string - }{ - Fullname: "Jane Doe", - }) + Name string + }{"Jane Doe"}) - // The same applies to maps; with maps there is no restriction on the case of key names. + // The same applies to maps; with maps there is no restriction on the + // case of key names. t2.Execute(os.Stdout, map[string]string{ - "Fullname": "Mickey Mouse", + "Name": "Mickey Mouse", }) - // You can use if control structure to show data conditionally. - // The data between if block will be shown if the field is truthy. - // Means it is not false boolean, empty string, nil or zero length slice, nil map/pointer. - t3, _ := template. - New("t3"). - Parse(`{{if .Field1}} - If block => {{.Field1}} - {{ else if .Field2}} - Else if block => {{.Field2}} - {{ else }} - Else block - {{ end }}`) + // if/else provide conditional execution for templates. A value is considered + // false if it's the default value of a type, such as 0, an empty string, + // nil pointer, etc. + // This sample demonstrates another + // feature of templates: using `-` in actions to trim whitespace. + t3 := Create("t3", + "{{if . -}} yes {{else -}} no {{end}}\n") + t3.Execute(os.Stdout, "not empty") + t3.Execute(os.Stdout, "") - s := struct { - Field1 string - Field2 []string - }{} - - s.Field1 = "" - s.Field2 = []string{} - t3.Execute(os.Stdout, s) - - s.Field1 = "Some text" - s.Field2 = nil - t3.Execute(os.Stdout, s) - - // Using a range action you can loop through a slice. - // Each time the range block is getting executed dot will be set - // to current item of slice. - t4, _ := template. - New("t4"). - Parse(`Range: {{ range . }} - {{.}} - {{ end }}`) + // range blocks let us loop through slices, arrays, maps or channels. Inside + // the range block `{{.}}` is set to the current item of the iteration. + t4 := Create("t4", + "Range: {{range .}}{{.}} {{end}}\n") t4.Execute(os.Stdout, []string{ "Go", @@ -102,14 +80,4 @@ func main() { "C++", "C#", }) - - // You can assign and reassign a value to a variable in templates. - t5, _ := template. - New("t5"). - Parse(`Variables: - {{ $language := "go" }} - {{ $language }} - {{ $language = "C" }} - {{ $language }}`) - t5.Execute(os.Stdout, nil) } diff --git a/examples/text-templates/text-templates.hash b/examples/text-templates/text-templates.hash index d0418a6..859241b 100644 --- a/examples/text-templates/text-templates.hash +++ b/examples/text-templates/text-templates.hash @@ -1,2 +1,2 @@ -69a28314f7ebd877b184b35a8166e2fcaab56754 --mRr-NuSB6f +c29676a83f4832a77b7a9e300d3fb5fe315de7b8 +pDwkw1iMACF diff --git a/examples/text-templates/text-templates.sh b/examples/text-templates/text-templates.sh index 3cf70b9..4c951f1 100644 --- a/examples/text-templates/text-templates.sh +++ b/examples/text-templates/text-templates.sh @@ -1,24 +1,9 @@ $ go run templates.go -Value is t1 -Value is some text -Value is true -Value is 5 -Value is [Go Rust C++ C#] -Value is {Jane Doe} -Fullname: Jane Doe -Fullname: Mickey Mouse - - Else block - - Else if block => Some text -Range: - Go - - Rust - - C++ - - C# -Variables: -go -C +Value: some text +Value: 5 +Value: [Go Rust C++ C#] +Name: Jane Doe +Name: Mickey Mouse +yes +no +Range: Go Rust C++ C# diff --git a/public/text-templates b/public/text-templates index 1f03b49..2b97618 100644 --- a/public/text-templates +++ b/public/text-templates @@ -27,8 +27,10 @@ -

Go offers built-in support for creating -dynamic content or showing customized output to the user called Template.

+

Go offers built-in support for creating dynamic content or showing customized +output to the user with the text/template package. A sibling package +named html/template provides the same API but has additional security +features and should be used for generating HTML.

@@ -42,7 +44,7 @@ dynamic content or showing customized output to the user called Template.

- +
package main
 
@@ -50,17 +52,11 @@ dynamic content or showing customized output to the user called Template.

-

Go has two template packages. one is “text/template” for -regular text manipulation, and another one is “html/template” -which has the same API as “text/template” but has additional security features. -It should be used when generating HTML.

- + -
-import (
-    "log"
+          
import (
     "os"
     "text/template"
 )
@@ -81,29 +77,19 @@ It should be used when generating HTML.

-

New creates a template with a specific name and returns a pointer to it.

+

We can create a new template and parse its body from +a string. +Templates are a mix of static text and “actions” enclosed in +{{...}} that are used to dynamically insert content.

     t1 := template.New("t1")
-
- - - - - -

Parse parses its parameter as template body. -We use {{.}} to access the value passed to the template when it’s getting executed.

- - - - -
     t1, err := t1.Parse("Value is {{.}}\n")
     if err != nil {
-        log.Fatal(err)
+        panic(err)
     }
 
@@ -111,29 +97,30 @@ We use {{.}} to access the value passed to the template when it’s getting -

If we want to ignore the errors we can use Must function. -It will panic if an error occurs when parsing the template.

+

Alternatively, we can use the template.Must function to +panic in case Parse returns an error. This is especially +useful for templates initialized in the global scope.

-    t1 = template.Must(t1.Parse("Value is {{.}}\n"))
+    t1 = template.Must(t1.Parse("Value: {{.}}\n"))
 
-

Execute applies parsed template to the data we pass to it and writes the output to the io.Writer.

+

By “executing” the template we generate its text with +specific values for its actions. The {{.}} action is +replaced by the value passed as a parameter to Execute.

-    t1.Execute(os.Stdout, t1.Name())
     t1.Execute(os.Stdout, "some text")
-    t1.Execute(os.Stdout, true)
     t1.Execute(os.Stdout, 5)
     t1.Execute(os.Stdout, []string{
         "Go",
@@ -141,25 +128,36 @@ It will panic if an error occurs when parsing the template.

"C++", "C#", }) - t1.Execute(os.Stdout, struct{ name string }{ - name: "Jane Doe", - })
-

If the data is a struct we can use the {{.FieldName}} action to access its fields. -The fields should be exported to be accessible when template is executing.

+

Helper function we’ll use below.

-    t2, _ := template.
-        New("t2").
-        Parse("Fullname: {{.Fullname}}\n")
+    Create := func(name, t string) *template.Template {
+        return template.Must(template.New(name).Parse(t))
+    }
+
+ + + + + +

If the data is a struct we can use the {{.FieldName}} action to access +its fields. The fields should be exported to be accessible when a +template is executing.

+ + + + +
+    t2 := Create("t2", "Name: {{.Name}}\n")
 
@@ -171,24 +169,23 @@ The fields should be exported to be accessible when template is executing.

    t2.Execute(os.Stdout, struct {
-        Fullname string
-    }{
-        Fullname: "Jane Doe",
-    })
+        Name string
+    }{"Jane Doe"})
 
-

The same applies to maps; with maps there is no restriction on the case of key names.

+

The same applies to maps; with maps there is no restriction on the +case of key names.

     t2.Execute(os.Stdout, map[string]string{
-        "Fullname": "Mickey Mouse",
+        "Name": "Mickey Mouse",
     })
 
@@ -196,82 +193,35 @@ The fields should be exported to be accessible when template is executing.

-

You can use if control structure to show data conditionally. -The data between if block will be shown if the field is truthy. -Means it is not false boolean, empty string, nil or zero length slice, nil map/pointer.

+

if/else provide conditional execution for templates. A value is considered +false if it’s the default value of a type, such as 0, an empty string, +nil pointer, etc. +This sample demonstrates another +feature of templates: using - in actions to trim whitespace.

-    t3, _ := template.
-        New("t3").
-        Parse(`{{if .Field1}}
-                    If block => {{.Field1}}
-                {{ else if .Field2}}
-                    Else if block => {{.Field2}}
-                {{ else }}
-                    Else block
-                {{ end }}`)
+    t3 := Create("t3",
+        "{{if . -}} yes {{else -}} no {{end}}\n")
+    t3.Execute(os.Stdout, "not empty")
+    t3.Execute(os.Stdout, "")
 
- - - - -
    s := struct {
-        Field1 string
-        Field2 []string
-    }{}
-
- - - - - - - - - -
    s.Field1 = ""
-    s.Field2 = []string{}
-    t3.Execute(os.Stdout, s)
-
- - - - - - - - - -
    s.Field1 = "Some text"
-    s.Field2 = nil
-    t3.Execute(os.Stdout, s)
-
- - - - - -

Using a range action you can loop through a slice. -Each time the range block is getting executed dot will be set -to current item of slice.

+

range blocks let us loop through slices, arrays, maps or channels. Inside +the range block {{.}} is set to the current item of the iteration.

- +
-    t4, _ := template.
-        New("t4").
-        Parse(`Range: {{ range . }}
-                        {{.}}
-                      {{ end }}`)
+    t4 := Create("t4",
+        "Range: {{range .}}{{.}} {{end}}\n")
     t4.Execute(os.Stdout,
         []string{
             "Go",
@@ -279,25 +229,6 @@ to current item of slice.

"C++", "C#", }) -
- - - - - -

You can assign and reassign a value to a variable in templates.

- - - - -
-    t5, _ := template.
-        New("t5").
-        Parse(`Variables: {{ $language := "go" }}
-                    {{ $language }}
-                    {{ $language = "C" }}
-                    {{ $language }}`)
-    t5.Execute(os.Stdout, nil)
 }
 
@@ -307,76 +238,21 @@ to current item of slice.

- - - - - - - - - - - - - - - - - - - - - - - - - @@ -395,7 +271,7 @@ to current item of slice.

- - - -
$ go run templates.go 
-Value is my-template
-Value is some text
-Value is true
-Value is 5
-Value is [Go Rust C++ C#]
-Value is {Jane Doe}
-Fullname: Jane Doe
-Fullname: Mickey Mouse
-
- - - -
        Else block
-
- - - -
        Else if block => Some text
-Range: 
-        Go
-
- - - -
        Rust
-
- - - -
        C++
-
-
        C#
-Variables: 
-go
-C
+
$ go run templates.go 
+Value: some text
+Value: 5
+Value: [Go Rust C++ C#]
+Name: Jane Doe
+Name: Mickey Mouse
+yes 
+no 
+Range: Go Rust C++ C#