Template layouts in revel - go

Is it possible to use template layouts in Revel. For example have a root.html which contains a {{define "main"}}{{end}} tag. Then call ExecuteTemplate(out, "main", nil) http://golang.org/pkg/html/template/#Template.ExecuteTemplate
Something like c.RenderLayout()

You can define the do you want into conf/routes file.
Then you can call you action with the name you want:
package controllers
import (
"github.com/revel/revel"
)
func (c Name) root() revel.Result {
c.Render()
}
views/Name/root.html
Other way can be this:
views/Controler/NameAction.html
{{template "root.html" .}}
views/root.html
<p>bla, bla</p>

Related

Create "x" amount of html elements in template based on how many elements I have in DB

I need to create an html page that display all the "forums" present in the database in my .html file.
Example:
<body>
{{with index . 0}}
{{.Name}}<br>{{.Descr}}</td>
{{end}}
{{with index . 1}}
{{.Name}}<br>{{.Descr}}
{{end}}
</body>
func index(w http.ResponseWriter, r *http.Request) {
forums := GetForumsFromDB() // return a slice of type Forum from the db
tpl.ExecuteTemplate(w, "index.html", forums)
}
type Forum struct {
Id int
Name string
Descr string
}
But in this case I need to already know how many forums are there in the db when writing the .html file. How should I approach this? Should I pass the html into the template together with my slice? Should I use a method of Forum that return the html for every forum?
Use range:
{{range .}}
{{.Name}}<br>{{.Descr}}
{{end}}

Go How can I use JavaScript code on my html page

Problem: I have script src tag in my index.html page but on browser this script doesn't load ( actually it loads but looks like index.html file 0_0 see pictures below ). The question is: How can I use JavaScript code which is in different file ( myscripts.js ) on my html page ?
js file looks like html ( index.html ) file
During googling I found "solution" but I does not work completely like it should. I just added this line to viewHandler method :
http.ServeFile(w, r, r.URL.Path[1:])
After this, project started look like this:
index.html after adding line above
To sum up:
My goal is to show html page and use javascript function which are in scripts/ folder.
Project Structure :
-scripts
--myscripts.js
-templates
--index.html
server.go
Server.go :
func viewHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("listening . . .")
tpl.ExecuteTemplate(w, "index.html", nil)
}
func main() {
http.HandleFunc("/", viewHandler)
http.ListenAndServe(":8080", nil)
}
myscripts.js:
function clickFunc(){
console.log("clicked")
}
index.html:
<head>
<script type="text/javascript" src="scripts/myscripts.js"></script>
</head>
<body>
<button onclick="clickFunc()">Click me</button>
</body>
</html>
You should be able to serve static files from a specific directory using the http package.
For example:
func main() {
ScriptsDirectory := http.FileServer(http.Dir("scripts"))
http.Handle("/scripts", ScriptsDirectory)
log.Println("Listening at port 3000")
http.ListenAndServe(":3000", nil)
}
Will let you server the file directly from that directory.
From there you can reference the /scripts directory in your index.html page as is.
Here is also a tutorial where this technique is used.

How to use base template file for golang html/template?

Have gin-gonic web app.
There are 3 files:
1) base.html -- base layout file
<!DOCTYPE html>
<html lang="en">
<body>
header...
{{template "content" .}}
footer...
</body>
</html>
2) page1.html, for /page1
{{define "content"}}
<div>
<h1>Page1</h1>
</div>
{{end}}
{{template "base.html"}}
3) page2.html, for /page2
{{define "content"}}
<div>
<h1>Page2</h1>
</div>
{{end}}
{{template "base.html"}}
The problem is that /page1 and /page2 use one template - page2.html. I think that I have misunderstanding of such constructions: {{define "content"}}, {{template "base.html"}}.
Please, can you show an example how to use base layouts in golang?
You can use the base.html as long as you parse the template along with your "content", like so:
base.html
{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<body>
header...
{{template "content" .}}
footer...
</body>
</html>
{{end}}
page1.html
{{define "content"}}
I'm page 1
{{end}}
page2.html
{{define "content"}}
I'm page 2
{{end}}
then ParseFiles with ("your-page.html", "base.html"), and ExecuteTemplate with your context.
tmpl, err := template.New("").ParseFiles("page1.html", "base.html")
// check your err
err = tmpl.ExecuteTemplate(w, "base", yourContext)
Go 1.16 introduces the embed package, which packages non-.go files into binaries, greatly facilitating the deployment of Go programs. The ParseFS function was also added to the standard library html/template, which compiles all the template files contained in embed.FS into a template tree.
// templates.go
package templates
import (
"embed"
"html/template"
)
//go:embed views/*.html
var tmplFS embed.FS
type Template struct {
templates *template.Template
}
func New() *Template {
funcMap := template.FuncMap{
"inc": inc,
}
templates := template.Must(template.New("").Funcs(funcMap).ParseFS(tmplFS, "views/*.html"))
return &Template{
templates: templates,
}
}
// main.go
t := templates.New()
t.templates is a global template that contains all matching views/*.html templates, all of which are related and can be referenced to each other, and the name of the template is the name of the file, e.g. article.html.
Further, we define a Render method for the *Template type, which implements the Renderer interface of the Echo web framework.
// templates.go
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
You can then specify the renderer for Echo to facilitate generating HTML responses at each handler, simply by passing the name of the template to the c.Render function.
// main.go
func main() {
t := templates.New()
e := echo.New()
e.Renderer = t
}
// handler.go
func (h *Handler) articlePage(c echo.Context) error {
id := c.Param("id")
article, err := h.service.GetArticle(c.Request().Context(), id)
...
return c.Render(http.StatusOK, "article.html", article)
}
Since the t.templates template contains all the parsed templates, each template name can be used directly.
In order to assemble HTMLs, we need to use template inheritance. For example, define a layout.html for the basic HTML frame and the <head> element, and set {{block "title"}} and {{block "content"}}, other templates inherit layout.html, and populate or override the layout template's blocks of the same name with their own defined blocks.
The following is the content of the layout.html template.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{block "title" .}}{{end}}</title>
<script src="/static/main.js"></script>
</head>
<body>
<div class="main">{{block "content" .}}{{end}}</div>
</body>
</html>
For other templates, you can refer to (inherit from) layout.html and define the blocks in the layout.html template.
For example, login.html reads as follows.
{{template "layout.html" .}}
{{define "title"}}Login{{end}}
{{define "content"}}
<form class="account-form" method="post" action="/account/login" data-controller="login">
<div div="account-form-title">Login</div>
<input type="phone" name="phone" maxlength="13" class="account-form-input" placeholder="Phone" tabindex="1">
<div class="account-form-field-submit ">
<button type="submit" class="btn btn-phone">Login</button>
</div>
</form>
{{end}}
article.html also references layout.html:
{{template "layout.html" .}}
{{define "title"}}<h1>{{.Title}}</h1>{{end}}
{{define "content"}}
<p>{{.URL}}</p>
<article>{{.Content}}</article>
{{end}}
We would expect the blocks defined in the login.html template to override the blocks in layout.html when rendering it, and also when rendering the article.html template. But that's not the case, and it's down to the Go text/template implementation. In our implementation of ParseFS(tmplFS, "views/*.html"), suppose article.html is parsed first and its content block is parsed as a template name, then when the login.html template is parsed later and acontent block is also found in it, text/template will overwrite the template of the same name with the later parsed content, so when all the templates are parsed, there is actually only one template named content in our template tree, which is the content defined in the last parsed template file.
Therefore, when we execute the article.html template, it is possible that the content template is not the content defined in this template, but the content defined in other templates.
The community has proposed some solutions to this problem. For example, instead of using a global template, a new template is created each time it is rendered, containing only the contents of layout.html and the sub-template. But this is really tedious. In fact, when Go 1.6 introduced the block directive [1] for text/template, we were able to do what we wanted with the Clone method, with just a few changes to the code above.
// templates.go
package templates
import (
"embed"
"html/template"
"io"
"github.com/labstack/echo/v4"
)
//go:embed views/*.html
var tmplFS embed.FS
type Template struct {
templates *template.Template
}
func New() *Template {
funcMap := template.FuncMap{
"inc": inc,
}
templates := template.Must(template.New("").Funcs(funcMap).ParseFS(tmplFS, "views/*.html"))
return &Template{
templates: templates,
}
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
tmpl := template.Must(t.templates.Clone())
tmpl = template.Must(tmpl.ParseFS(tmplFS, "views/"+name))
return tmpl.ExecuteTemplate(w, name, data)
}
You can see that only the Render function has been modified here. Instead of executing the global template, we will clone it into a new template, and the content block in this new template may not be the one we want, so here we parse the content of a sub-template we will eventually render on top of this global template, so that the content of the newly added sub-template will overwrite the previous, possibly incorrect content. Our target sub-template references layout.html in the global template, which is not conflicted, and since the global template is never executed (we clone a new global template in the Render function each time it is executed), it is also clean. When a template is finally executed, we have a clean layout.html with the content content we want, which is equivalent to generating a new template each time we execute it, which contains only the layout template and sub-templates we need. The idea is the same, but instead of manually generating a new template when executing the template, it's done automatically in the Render function.
Of course, you can also use {{ template }} to refer to other layout templates in the sub-template, as long as these layout templates do not overwrite each other, you just need to specify the name of the target sub-template when executing, and the template engine will automatically use the {{ template }} tag defined in it to find the layout templates for us, which are all in the cloned global template.
[1] https://github.com/golang/go/commit/12dfc3bee482f16263ce4673a0cce399127e2a0d
As far as I understand, when you use ParseGlob(), Gin parses all the matching files and creates a single template object from them. For doing what you want, you'd need two different templates (one for page 1, another for page 2).
Gin documentation says this is a known limitation and points the way to overcome it:
Gin allow by default use only one html.Template. Check a multitemplate render for using features like go 1.6 block template.
Using the multitemplate library, you can write something like this:
render := multitemplate.NewRenderer()
render.AddFromFiles("page1", "templates/base.html", "templates/page1.html")
render.AddFromFiles("page2", "templates/base.html", "templates/page2.html")
router := gin.Default()
router.HTMLRender = render
// Later
ginContext.HTML(200, "page1", gin.H{
"title": "The Wonderful Page One",
})
This requires more manual setup than I'd hope for, but gets the job done.
The easiest way which avoids a map and works in a single template:
base.html
<!DOCTYPE html>
<html lang="en">
<body>
header...
{{block "content" .}}{{end}}
footer...
</body>
</html>
page1.html
{{template "base.html" .}}
{{define "content"}}This is page 1{{end}}
page2.html
{{template "base.html" .}}
{{define "content"}}This is page 2{{end}}
t := template.Must(template.ParseGlob("*.html"))
err := t.ExecuteTemplate(w, "page1.html", context)
err := t.ExecuteTemplate(w, "page2.html", context)

Render a partial template with passed parameters

I know rendering a partial template with additional parameters is possible in Ruby, how can I do it in Go?
I have a partial template _partial1.tmpl:
<div>
text1
{{if foo}}
text2
{{end}}
</div>
using it from the parent template parent.tmpl:
<div>
{{ template "partial1", }} // how do I pass foo param here??
</div>
How do I pass the parameter foo to the partial?
The documentation states that the template directive has two forms:
{{template "name"}}
The template with the specified name is executed
with nil data.
{{template "name" pipeline}}
The template with the specified name is
executed with dot set to the value of the pipeline.
The latter accepts a pipeline statement which's value is then set to the dot value in the executed template. So calling
{{template "partial1" "string1"}}
will set {{.}} to "string1" in the partial1 template. So while there is no way to set the name foo in the partial, you can pass parameters and they will appear in .. Example:
template.html
<div>
{{ template "partial1.html" "muh"}} // how do I pass foo param here??
</div>
partial1.html
{{if eq . "muh"}}
blep
{{else}}
moep
{{end}}
main.go
import (
"html/template"
"fmt"
"os"
)
func main() {
t,err := template.ParseFiles("template.html", "partial1.html")
if err != nil { panic(err) }
fmt.Println(t.Execute(os.Stdout, nil))
}
Running this program will print the template's contents with blep from the partial. Changing the passed value will change this behaviour.
You can also assign variables, so assigning . to foo is possible in the partial:
{{ $foo := . }}

Multiple files using template.ParseFiles in golang

For example.go, I have
package main
import "html/template"
import "net/http"
func handler(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("header.html", "footer.html")
t.Execute(w, map[string] string {"Title": "My title", "Body": "Hi this is my body"})
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
In header.html:
Title is {{.Title}}
In footer.html:
Body is {{.Body}}
When going to http://localhost:8080/, I only see "Title is My title", and not the second file, footer.html. How can I load multiple files with template.ParseFiles? What's the most efficient way to do this?
Thanks in advance.
Only the first file is used as the main template. The other template files need to be included from the first like so:
Title is {{.Title}}
{{template "footer.html" .}}
The dot after "footer.html" passes the data from Execute through to the footer template -- the value passed becomes . in the included template.
There is a little shortcoming in user634175's method: the {{template "footer.html" .}} in the first template must be hard coded, which makes it difficult to change footer.html to another footer.
And here is a little improvement.
header.html:
Title is {{.Title}}
{{template "footer" .}}
footer.html:
{{define "footer"}}Body is {{.Body}}{{end}}
So that footer.html can be changed to any file that defines "footer", to make different pages

Resources