I have struct that has JSON field something like this:
detail := &Detail{
Name string
Detail json.RawMessage
}
template looks like this:
detail = At {{Name}} {{CreatedAt}} {{UpdatedAt}}
My question can we use one or more structs for a single template or it is restricted to only one struct.
You can pass as many things as you like. You haven't provided much of an example to work with, so I'm going to assume a few things, but here's how you would tackle it:
// Shorthand - useful!
type M map[string]interface
func SomeHandler(w http.ResponseWriter, r *http.Request) {
detail := Detail{}
// From a DB, or API response, etc.
populateDetail(&detail)
user := User{}
populateUser(&user)
// Get a session, set headers, etc.
// Assuming tmpl is already a defined *template.Template
tmpl.RenderTemplate(w, "index.tmpl", M{
// We can pass as many things as we like
"detail": detail,
"profile": user,
"status": "", // Just an example
}
}
... and our template:
<!DOCTYPE html>
<html>
<body>
// Using "with"
{{ with .detail }}
{{ .Name }}
{{ .CreatedAt }}
{{ .UpdatedAt }}
{{ end }}
// ... or the fully-qualified way
// User has fields "Name", "Email", "Address". We'll use just two.
Hi there, {{ .profile.Name }}!
Logged in as {{ .profile.Email }}
</body>
</html>
Hope that clarifies.
Related
i try to loop over a dic in my jinja template. But i always get no output.
dic={"SERVICE1" : "tess","SERVICE2" : "test"}
with open(Path(Path.cwd()/"test.jinja")) as f:
template = Template(f.read()).render(data_nebula_docker)
print(template)
My template looks like this:
{% for item in dic %}
{{item}}
{% endfor %}
Normal Variablse are working fine. Does i have some basic misunderstanding?
Thx for your help!
There are a couple of problems with your code. I'm going to assume the indentation issue in your with statement is just a copy-and-paste error, and that your code actually looks like:
with open(Path(Path.cwd()/"test.jinja")) as f:
template = Template(f.read()).render(data_nebula_docker)
print(template)
In your template you're referencing a variable dic, but you aren't passing this to the render method. You want:
from jinja2 import Template
from pathlib import Path
dic = {"SERVICE1": "tess", "SERVICE2": "test"}
with open(Path(Path.cwd() / "test.jinja")) as f:
template = Template(f.read()).render(dic=dic)
print(template)
Which produces:
SERVICE1
SERVICE2
You'll note that this only prints out the keys in your dictionary. When you iterate over a dictionary (in Jinja or Python), you get a list of keys. If you want (key, value) tuples instead, use the .items() method:
{% for item in dic.items() %}
{{item}}
{% endfor %}
Which produces:
('SERVICE1', 'tess')
('SERVICE2', 'test')
Separately, your use of pathlib.Path is a little convoluted; you can write instead:
with (Path.cwd()/"test.jinja").open() as f:
In the above:
It's not necessary to wrap the result of Path() / "string" in a call to Path() (because it's already a Path)
A Path object has an open method, so we can call .open() on the Path that results from Path.cwd()/"test.jinja".
Although in this case, your pathlib based code isn't much of a win over:
with open("test.jinja") as f:
In this code, I want to use and give a specific detail in the HTML file like heading or price.
The problem is that, there are multiple headings and prices and when I print the specific one, it prints the specific data successfully but I don't know how to use it in an HTML file to print the specific data there. All I know about GOHTML is {{.Heading}} but it does not work. Is there any other way?
package main
import "net/http"
type Details struct {
Heading string
Price string
}
var Detail = []Details{
{
Heading: "First Cloth",
Price: "$59",
},
{
Heading: "Second Cloth",
Price: "$49",
},
}
func Main(w http.ResponseWriter, r *http.Request) {
HomeTmpl.Execute(w, Detail)
// fmt.Println(Detail[1].Heading) // For specific data
}
You may use the builtin index template function to get an element from a slice:
{{ (index . 1).Heading }}
Testing it:
t := template.Must(template.New("").Parse(`{{ (index . 1).Heading }}`))
if err := t.Execute(os.Stdout, Detail); err != nil {
panic(err)
}
Which outputs (try it on the Go Playground):
Second Cloth
I want to show a table that each row contains my struct data.
Here is my struct:
type My_Struct struct {
FIRST_FIELD string
SECOND_FIELD string
THIED_FIELD string
}
Here is my html code:
<table id="t01">
<tr>
<th>FIRST FIELD</th>
<th>SECOND FIELD</th>
<th>THIRD FIELD</th>
</tr>
<tr>
<td>FIRST_OBJ_HERE_SHOULD_BE_THE_FIRST_FIELD</td>
<td>FIRST_OBJ_HERE_SHOULD_BE_THE_SECOND_FIELD</td>
<td>FIRST_OBJ_HERE_SHOULD_BE_THE_THIRD_FIELD</td>
</tr>
<tr>
<td>SECOND_OBJ_HERE_SHOULD_BE_THE_FIRST_FIELD</td>
<td>SECOND_OBJ_HERE_SHOULD_BE_THE_SECOND_FIELD</td>
<td>SECOND_OBJ_HERE_SHOULD_BE_THE_THIRD_FIELD</td>
</tr>
</table>
As you see, I want to pass a slice with my struct (each one contains 3 files) to this html code, and I want the the whole slice will be set in this table - each row contains one struct data.
How can I achieve this?
It seems like you want the Go Template package.
Here's an example of how you may use it:
Define a handler that passes an instance of a struct with some defined field(s) to a view that uses Go Templates:
type MyStruct struct {
SomeField string
}
func MyStructHandler(w http.ResponseWriter, r *http.Request) {
ms := MyStruct{
SomeField: "Hello Friends",
}
t := template.Must(template.ParseFiles("./showmystruct.html"))
t.Execute(w, ms)
}
Access the struct fields using the Go Template syntax in your view (showmystruct.html):
<!DOCTYPE html>
<title>Show My Struct</title>
<h1>{{ .SomeField }}</h1>
Update
If you are interested particularly in passing a list, and iterating over that, then the {{ range }} keyword is useful. Also, there's a pretty common pattern (at least in my world) where you pass a PageData{} struct to the view.
Here's an expanded example, adding a list of structs, and a PageData struct (so we can access its fields in the template):
type MyStruct struct {
SomeField string
}
type PageData struct {
Title string
Data []MyStruct
}
func MyStructHandler(w http.ResponseWriter, r *http.Request) {
data := PageData{
Title: "My Super Awesome Page of Structs",
Data: []MyStruct{
MyStruct{
SomeField: "Hello Friends",
},
MyStruct{
SomeField: "Goodbye Friends",
},
}
t := template.Must(template.ParseFiles("./showmystruct.html"))
t.Execute(w, data)
}
And the modified template (showmystruct.html):
<!DOCTYPE html>
<title>{{ .Title }}</title>
<ul>
{{ range .Data }}
<li>{{ .SomeField }}</li>
{{ end }}
</ul>
I'm trying to define a custom Go func to use in a template condition.
What I want to achieve is : if the given argument is an IPv4 address the template will output IPv4: [argument] else it will output IPv6: [argument].
To do that I have created a template like this:
{{ range .Addresses }}
{{ if IsIPv4 . }}
IPv4: {{ . }}
{{ else }}
IPv6: {{ . }}
{{ end }}
{{ end }}
As you can see I have create a new function called IsIPv4 which take a string argument and given the argument return true or false. Here is the code:
var (
errNotAnIPAddress = errors.New("The argument is not an IP address")
)
func isIPv4(address string) (bool, error) {
ip := net.ParseIP(address)
if ip == nil {
return false, errNotAnIPAddress
}
return (ip.To4() != nil), nil
}
When executing my template I have got no errors but the execution looks like to stop when trying to evaluate {{ if IsIPv4 . }}.
Of course the function is mapped before trying to parse and execute the template.
funcMap := template.FuncMap{
"IsIPv4": isIPv4,
}
I'm pretty new to Go and I have probably missed something (maybe obvious?).
To debug a little bit I have tried to remove the IsIPv4 call in my template giving a condition like {{ if . }}. And the result was to always output IPv4: [the IP address]. That makes sense to me.
I also took a look at the predefined Go funcs like eq and it looks like I'm trying to reproduce the same idea, without any success.
TL;DR; You must inspect the error value returned by template.Execute() or template.ExecuteTemplate() which will tell you why it doesn't work for you.
Things that could go wrong
First, your execution doesn't panic because it simply returns an error. Would you inspect that, you'll probably know what's going wrong immediately:
if err := t.Execute(os.Stdout, data); err != nil {
panic(err)
}
Next what could go wrong: you have to register your custom functions prior to parsing the template because the template engine needs to be able to statically analyze the template, and it needs to know prior that IsIPv4 is a function name:
t := template.Must(template.New("").Funcs(template.FuncMap{
"IsIPv4": isIPv4,
}).Parse(templ))
Another potential error is that isIPv4() expects a value of type string, and you may pass a value of different type.
And custom functions registered for templates should only return a non-nil error if you intend to stop the execution of the template. Because that's what happens if you return an error. template.FuncMap:
[...] if the second (error) return value evaluates to non-nil during execution, execution terminates and Execute returns that error.
Working example
Here's a working example using your template and your isIPv4() function:
t := template.Must(template.New("").Funcs(template.FuncMap{
"IsIPv4": isIPv4,
}).Parse(templ))
m := map[string]interface{}{
"Addresses": []string{"127.0.0.1", "0:0:0:0:0:0:0:1"},
}
if err := t.Execute(os.Stdout, m); err != nil {
panic(err)
}
Output (try it on the Go Playground):
IPv4: 127.0.0.1
IPv6: 0:0:0:0:0:0:0:1
Potential errors
The above program prints the following errors:
Passing an invalid IP:
"Addresses": []string{"127.0.0.1.9"},
Output:
panic: template: :2:6: executing "" at <IsIPv4 .>: error calling IsIPv4:
The argument is not an IP address
Passing a non-string value:
"Addresses": []interface{}{2},
Output:
panic: template: :2:13: executing "" at <.>: wrong type for value; expected string; got int
Attempting to register your custom function after the template has been parsed:
t := template.Must(template.New("").Parse(templ))
t.Funcs(template.FuncMap{
"IsIPv4": isIPv4,
})
Output:
panic: template: :2: function "IsIPv4" not defined
I use the Martini framework,I have some markdown file and I want render it as HTML in tmpl/html template.
The markdown file like this:
title: A Test Demo
---
##ABC
> 123
And the template file like this:
<head>
<title>{{name}}</title>
</head>
<body>
<h2>{{abc}}</h2>
<blockquote>
<p>{{xyz}}</p>
</blockquote>
</body>
I use the blackfriday parse the markdown and return []byte type,next step I wanna render the markdown file to this template and make each block to the right place,so how can I do this right way? Or use any way to do this better?
One way to achieve this is to use the Funcs method to add a custom function to the template function map. See the Functions section of the template package docs for more info.
Given a template file page.html, some writer w (probably an http.ResponseWriter), and some struct p with a field Body containing data to be put into a template field, you can do something like:
Define a function:
func markDowner(args ...interface{}) template.HTML {
s := blackfriday.MarkdownCommon([]byte(fmt.Sprintf("%s", args...)))
return template.HTML(s)
}
Add it to the template function map:
tmpl := template.Must(template.New("page.html").Funcs(template.FuncMap{"markDown": markDowner}).ParseFiles("page.html"))
Execute the template:
err := tmpl.ExecuteTemplate(w, "page.html", p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
Then, in your template file, you can put something like:
{{.Body | markDown}}
And it will pass the Body through your markDowner function.
Playground