Optimising html/template Composition - go

I'm looking to see if there is a better (faster, more organised) way to split up my templates in Go. I strongly prefer to stick to html/template (or a wrapper thereof) since I trust its security model.
Right now I use template.ParseGlob to parse all of my template files in within init().
I apply template.Funcs to the resulting templates
I set a $title in each template (i.e. listing_payment.tmpl) and pass this to the content template.
I understand that html/template caches templates in memory once parsed
My handlers only call t.ExecuteTemplate(w, "name.tmpl", map[string]interface{}) and don't do any silly parsing on each request.
I compose templates from multiple pieces (and this is the bit I find clunky) as below:
{{ $title := "Page Title" }}
{{ template "head" $title }}
{{ template "checkout" }}
{{ template "top" }}
{{ template "sidebar_details" . }}
{{ template "sidebar_payments" }}
{{ template "sidebar_bottom" }}
<div class="bordered-content">
...
{{ template "listing_content" . }}
...
</div>
{{ template "footer"}}
{{ template "bottom" }}
My three questions are:
Is this performant, or do the multiple {{ template "name" }} tags result in a potential per-request performance hit? I see a lot of write - broken pipe errors when stress testing heavier pages. This might just be due to socket timeouts (i.e. socket closing before the writer can finish) rather than some kind of per-request composition, though (correct me if otherwise!)
Is there a better way to do this within the constraints of the html/template package? The first example in Django's template docs approaches what I'd like. Extend a base layout and replace the title, sidebar and content blocks as needed.
Somewhat tangential: when template.ExecuteTemplate returns an error during a request, is there an idiomatic way to handle it? If I pass the writer to an error handler I end up with soup on the page (because it just continues writing), but a re-direct doesn't seem like idiomatic HTTP.

With some help on Reddit I managed to work out a fairly sensible (and performant) approach to this that allows:
Building layouts with content blocks
Creating templates that effectively "extend" these layouts
Filling in blocks (scripts, sidebars, etc.) with other templates
base.tmpl
<html>
<head>
{{ template "title" .}}
</head>
<body>
{{ template "scripts" . }}
{{ template "sidebar" . }}
{{ template "content" . }}
<footer>
...
</footer>
</body>
index.tmpl
{{ define "title"}}<title>Index Page</title>{{ end }}
// We must define every block in the base layout.
{{ define "scripts" }} {{ end }}
{{ define "sidebar" }}
// We have a two part sidebar that changes depending on the page
{{ template "sidebar_index" }}
{{ template "sidebar_base" }}
{{ end }}
{{ define "content" }}
{{ template "listings_table" . }}
{{ end }}
... and our Go code, which leverages the map[string]*template.Template approach outlined in this SO answer:
var templates map[string]*template.Template
var ErrTemplateDoesNotExist = errors.New("The template does not exist.")
// Load templates on program initialisation
func init() {
if templates == nil {
templates = make(map[string]*template.Template)
}
templates["index.html"] = template.Must(template.ParseFiles("index.tmpl", "sidebar_index.tmpl", "sidebar_base.tmpl", "listings_table.tmpl", "base.tmpl"))
...
}
// renderTemplate is a wrapper around template.ExecuteTemplate.
func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
// Ensure the template exists in the map.
tmpl, ok := templates[name]
if !ok {
return ErrTemplateDoesNotExist
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
tmpl.ExecuteTemplate(w, "base", data)
return nil
}
From initial benchmarks (using wrk) it seems to be a fair bit more performant when it comes to heavy load, likely due to the fact that we're not passing around a whole ParseGlob worth of templates every request. It also makes authoring the templates themselves a lot simpler.

Related

Execute multiple templates in go

I have this variable and I wanted to know if I can execute multiple templates
and if I can't do it with this variable then what should I do ?
var (
templates = template.Must(template.ParseGlob("web/Pages/*"))
)
Thanks in advance
You can include templates in other templates by using the {{ template "name" }} action.
Say you have file a.html:
<p>this is A</p>
then you can include it in b.html like this:
{{ template "a.html" }}
<p>this is B</p>
and then if you run templates.ExecuteTemplate(os.Stdout, "b.html", nil) you'll get:
<p>this is A</p>
<p>this is B</p>
See the playground link for a more fleshed out example: https://play.golang.com/p/W0whRWlX119

October CMS and navigation on current page

I am trying to get the basically the active class applied to the current page. As it goes, the builder plugin is setting the URL through:
<a href="{{ detailsPage|page({ (detailsUrlParameter): attribute(record, detailsKeyColumn) }) }}">
However I am new to October so I am not sure how to reference this.page.id in comparison to the url set above.
Basically I want this:
{ set UrlParam = detailsPage|page({ (detailsUrlParameter): attribute(record, detailsKeyColumn) }
{% if this.page.id == UrlParam %} class="active" {% endif %}
Any ideas?
One of the best debugging plugins out there for OctoberCMS is this: https://octobercms.com/plugin/davask-dump
That plugin makes connecting your twig templates to your database / php rendering a breeze.
After installing that plugin you can use {{ d(variable) }} instead of {{ dd(variable) }} and get more information on the array nests etc.
So I would do {{ d(UrlParam) }} and {{ d(this.page.id) }} in your twig template. See what the dump has to say about each of those variables. For clarity I do believe you need the % here {**%** set 'variable' **%**}.
I am also not a fan of the builder component and I use the PHP section on the page / partial pages. And establishing a class with the use function and access the data with $this['variable']. Maybe worth looking into here is a quick example:
Pluginauthor\Plugin\Models\Plugin;
function onStart() {
$plugin = Pluggin::all();
$this['plugin'] = $plugin;
}

Golang Accessing Template Variables From Included Templates [duplicate]

This question already has an answer here:
Variable in template's included template
(1 answer)
Closed 6 years ago.
In golang, I am working with three files: index.html, nav.html and main.go
nav.html contains the following:
{{ define "nav" }}
<nav class="nav-container">
<h1>{{ .path }}</h1>
</nav>
{{ end }}
index.html contains the following:
{{ define "index" }}
{{ template "nav" }} <!-- Includes the nav.html file -->
<h1>Welcome to my website. You are visiting {{ .path }}.</h1>
{{ end }}
I am using Golang's template package along with Martini which is not too important in this case.
My main.go file contains:
package main
import (
"net/http"
"github.com/go-martini/martini"
"github.com/martini-contrib/render"
)
func main() {
m := martiniSetup()
m.Get("/", func(res http.ResponseWriter, req *http.Request, ren render.Render, params martini.Params) {
parse := make(map[string]interface{})
parse["path"] = req.URL.Path
ren.HTML(http.StatusOK, "index", parse)
})
m.Run()
}
My problem:
The .path variable being parsed into the index template is only accessable by the index template itself.
I include the nav template using {{ template "nav" }} inside index.html. The issue is, nav.html cannot access the .path variable. It is only accessable by the index template.
Is there any way to make the .path variable accessable to all included template files, in my case index.html and nav.html?
You can pass the data to the nested template as an argument like this: {{ template "nav" . }}
Now the dot will be accessible within the define "nav" block.

Avoid to reset the variable's value inside loop in golang's view [duplicate]

I'm trying to use a variable declared outside a Go template range loop to see if the previous post occurred on the same day as the current post. Here's a simplified example.
Where .Posts is an array of post structs that each have a .Content and a .Date.
{{ $prevDate := "" }}
{{ range $post := .Posts }}
{{ if ne $prevDate $post.Date }}
<div class="post-date">Posts dated: {{ $post.Date }}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ $prevDate := $post.Date }}
{{ end }}
The problem is that $prevDate seems to be reset to "" at the start of each iteration of the loop.
Can anyone help me understand why the value of $prevDate is reset on each iteration and perhaps suggest a way to accomplish what I'm trying to do here?
Note: Go 1.11 will support modifying template variables via assignment. This will be valid code:
{{ $v := "init" }}
{{ if true }}
{{ $v = "changed" }}
{{ end }}
v: {{ $v }} {{/* "changed" */}}
Original answer pre-dating Go 1.11 follows:
Variables are not reset. Basically what happens is that you redeclare the $prevDate variable inside the loop. But it is only in scope after the redeclaration and before the closing {{end}} tag of the {{range}}. So when the next iteraiton of the loop comes, you only see the "outer" variable which you haven't changed (because you created a new).
You can't change the values of the template variables you create.
What you can do is for example use the following range form:
{{ range $index, $post := .Posts }}
And...
Solution #1: with a registered Function
And you can register a function for the template (see template.Funcs()) to which you can pass the $index and it would return the date field of the previous element (at $index -1).
It would look something like this:
func PrevDate(i int) string {
if i == 0 {
return ""
}
return posts[i-1].Date
}
// Registering it:
var yourTempl = template.Must(template.New("").
Funcs(map[string]interface{}{"PrevDate": PrevDate}).
Parse(yourStringTemplate))
And from your template you can call it like:
{{range $index, $post := .Posts}}
{{$prevDate := PrevDate $index}}
{{end}}
Solution #2: with a Method of Posts
This solution is analog but is even simpler: add a method to your Posts and you can call it directly. No need to register a function.
For example:
type Post struct {
// Your Post type
Date string
}
type Posts []Post
func (p *Posts) PrevDate(i int) string {
if i == 0 {
return ""
}
return (*p)[i-1].Date
}
And from your template you can call it like:
{{range $index, $post := .Posts}}
{{$prevDate := $.Posts.PrevDate $index}}
{{end}}
Go templates are not designed to support complex logic. There's the Go programming language for that. Templates have limitations as a consequence of this philosophy. One limitation is that template variables cannot be changed.
One way to handle this limitation is to structure the data in Go to match the structure of output. Create a type to hold posts for a date and render a slice of these types. The template simply ranges through PostsForDate and Posts.
type PostsForDate struct {
Date time.Time
Posts []*Post
}
var Dates []PostsForDate
{{range .Dates}}
<div class="post-date">Posts dated: {{.Date}}</div>
{{range .Posts}}
<div class="post-content">{{.Content}}</div>
{{end}}
{{end}}
A simpler option (that goes against the design philosophy to some degree) is to create a type in Go to record a current value and report changes to that value.
type change struct {
current interface{}
}
func (c *change) Changed(next interface{}) bool {
result := c.current != next
c.current = next
return result
}
func newChange() *change {
return &change{&struct{ int }{}} // initial value ensures that first change is fired.
}
and hook it into a template using a template function:
t := template.Must(template.New("").Funcs(template.FuncMap{"change": newChange}).Parse(` some template `))
Use it in a template like this:
{{ $i := change }}
{{ range $post := .Posts }}
{{ $i.Change $post.Date }}
<div class="post-date">Posts dated: {{ $post.Date }}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ end }}
playground example
If the post Date field is a time.Time and the posts have different times within a day, then the above does not work as desired. A workaround for this is to check for changes in the rendered date (for example $post.Date.Format "2006-01-02"). Add the following method to simplify this:
func (c *change) ChangedValue(next interface{}) interface{} {
if c.current != next {
c.current = next
return next
}
return nil
}
Use it like this:
{{ $i := change }}
{{ range $post := .Posts }}
{{with $i.ChangedValue ($post.Date.Format "2006-01-02")}}
<div class="post-date">Posts dated: {{.}}</div>
{{ end }}
<div class="post-content">{{ $post.Content }}</div>
{{ end }}
This only works when the values are guaranteed to be considered true by the template package.
This solution does not require parsing the template on every use (as in solution #1 in the other answer) and it applies to arbitrary slice types (unlike both solutions in the other answer).

Building a widget manager on top of Symfony 2 (multiple controllers in one page)

Use case
I am developing a CMF on top of Symfony2. One of the features will be the support of "widgets": an possibility for end users to add small 'blocks' or 'modules' to a page. Examples:
A small login form
A list of products
Some photo's from a gallery
A shopping cart
The idea is that most of those widgets will link to to normal, full page Routes/Controllers.
For example: a user want a list of popular products in a sidebar on a content page. The items will link to the normal /product/{name} route of the ProductController. But the list in this case would be a widget. The end user can define where it must be placed, and for example, how much items must be shown.
The behavior of the 'widgets' is the same as regular Symfony2 controllers, it has routes, actions, it renders a view, etcetera. There is a WidgetManager with a catch-all route to load the widgets, configure them and render them in the right place.
I have not that much experience with Symfony2, but I am playing wit it now for more then 3 months. I definitely want to stay with Symfony2, but will need to add some magic to realize some of my ideas.
Question
What is the best way to support rendering multiple controllers (widgets) in one request?
Research
Symfony's TwigExtension "ActionExtension" contains a "render" method, which contains the basic idea:
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
</div>
(Documentation: http://symfony.com/doc/current/book/templating.html#embedding-controllers)
But it is quite limited. Some problems with this approach:
I cannot configure the 'widgets' before rendering them (for example: $myWidget->set('show_toolbar', false)), I don't want to pass all options as controller action parameters.
It is not possible to use template inheritance. I need this for example for 'injecting' the asset references (javascript/css) in de base <HEAD> block.
What I want
I want the following code to work (this is a simplified example):
// Serius\PageBundle\Controller\PageController.php
// executed by a catch-all route
public function indexAction($url) {
// load CMS page, etc
$widgets = $this->loadWidgets($page); // widgets configuration is stored in database
// at this point, $widgets is an array of Controller *instances*
// meaning, they are already constructed and configured
return $this->render("SeriusPageBundle:Page:content.html.twig", array(
'widgets' => $widgets
));
}
Serius\PageBundle\Resources\views\Page\content.html.twig
{% extends 'SeriusPageBundle::layout.html.twig' %}
{% block content %}
{% for widget in widgets %}
<div>
{% render widget %}
<!-- Of course, this doesn't work, I would have to create my own Twig extension -->
</div>
{% endfor %}
{% endblock %}
An example of a widget template:
{% extends '::base.html.twig' %}
{% block stylesheets %}
My stylesheets
{% endblock %}
{% block body %}
This is a shoppingcart widget!
{% endblock %}
How can I achieve this? Do someone have experience with anything like this? I already looked at the Symfony CMF project, but it has no support for this (as far as I could find out).
I have something similar going around and I think that this code will help you. In chosen template you get the variables with the names of blocks.
public function render()
{
$modules = $this->moduleService->getModules();
foreach($modules as $m){
$templateName = $m->getTemplateName();
$template = $this->twig->loadTemplate($templateName);
$blockNames = $template->getBlockNames();
foreach($blockNames as $b){
if(isset($this->blocks[$b]) == false)
$this->blocks[$b] = '';
$this->blocks[$b] .= $template->renderBlock($b, array('a' => 'aaa', 'b' => 'bbb'));
}
}
$content = $this->twig->render('Admin/index.html.twig',$this->blocks);
return new \Symfony\Component\HttpFoundation\Response($content);
}
I know this is old, but if anyone is looking for something like this the SonataBlockBundle might be your solution.
https://github.com/sonata-project/SonataBlockBundle

Resources