Closed Allendar closed 7 years ago
@allendar I have been also researching about this since I have been using the default go template and it's horrible slow.
Any examples how to integrate quicktemplate with Siris?
Here's a raw example of how I'm doing my front-end:
layout.qtpl
{% import "espaldd.com/espal/translations" %}
{% interface
Page {
Title()
Menu()
Content()
Stylesheets()
Javascripts()
}
%}
{% code
type BasePage struct {
IsLoggedIn bool
LocaleID uint16
AdminUrl *string
}
%}
{% func PageTemplate(p Page) %}{% stripspace %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{%= p.Title() %}</title>
<link rel="stylesheet" href="/c/m.css">
{%= p.Stylesheets() %}
</head>
<body>{% stripspace %}
{%= p.Menu() %}
{%= p.Content() %}
{%= p.Javascripts() %}
{% endstripspace %}</body>
</html>
{% endstripspace %}{% endfunc %}
{% func (p *BasePage) Title() %}{% endfunc %}
{% func (p *BasePage) Menu() %}{% stripspace %}
<header>
<a href="/">{%s= translations.Fast(p.LocaleID, "home") %}</a>
<a href="/catalog">{%s= translations.Fast(p.LocaleID, "catalog") %}</a>
<a href="/forums">{%s= translations.FastPlural(p.LocaleID, "forum") %}</a>
{% if !p.IsLoggedIn %}<a href="/login">{%s= translations.Fast(p.LocaleID, "login") %}</a>{% endif %}
{% if p.IsLoggedIn %}<a href="/logout">{%s= translations.Fast(p.LocaleID, "logout") %}</a>{% endif %}
{% if !p.IsLoggedIn %}<a href="/forgot-password">{%s= translations.Fast(p.LocaleID, "forgot_password") %}</a>{% endif %}
{% if p.IsLoggedIn %}<a href="/{%s *p.AdminUrl %}">{%s= translations.Fast(p.LocaleID, "admin") %}</a>{% endif %}
</header>
{% endstripspace %}{% endfunc %}
{% func (p *BasePage) Content() %}{% endfunc %}
{% func (p *BasePage) Stylesheets() %}{% endfunc %}
{% func (p *BasePage) Javascripts() %}{% endfunc %}
login.qtpl
{% import "github.com/go-siris/siris/context" %}
{% import "espaldd.com/espal/validator" %}
{% import "espaldd.com/espal/translations" %}
{% code
type LoginPage struct {
BasePage
Context context.Context
Form map[string]validator.FormFieldType
FormErrors []string
}
%}
{% func (p *LoginPage) Stylesheets() %}<link rel="stylesheet" href="/c/simple-box.css">{% endfunc%}
{% func (p *LoginPage) Title() %}{%s= translations.Fast(p.LocaleID, "login") %}{% endfunc%}
{% func (p *LoginPage) Content() %}{% stripspace %}
<div class="simpleBox">
{%= FormErrors(p.FormErrors) %}
<h1>{%s= translations.Fast(p.LocaleID, "login") %}</h1>
<form method="post" onsubmit="return s(this)">
{%s= FormHiddenField(p.Form["_uname"]) %}
{%s= FormHiddenField(p.Form["_t"]) %}
{%s= FormEmailField(p.Form["email"]) %}<br>
{%s= FormPasswordField(p.Form["password"]) %}<br>
{%s= FormCheckboxField(p.Form["remember_me"]) %}<br><br>
<input type="submit" value="{%s= translations.Fast(p.LocaleID, "login") %}">
</form>
<p><a href="/forgot-password"><small>{%s= translations.Fast(p.LocaleID, "forgot_your_password") %}</small></a></p>
<p><a href="/register-account"><small>{%s= translations.Fast(p.LocaleID, "no_account_yet") %}</small></a></p>
</div>
{% endstripspace %}{% endfunc%}
{% func (p *LoginPage) Javascripts() %}<script src="/j/form.js"></script>{% endfunc%}
helpers.qtpl
{% import "espaldd.com/espal/validator" %}
{% import "espaldd.com/espal/database" %}
{% func FormErrors(errors []string) %}{% stripspace %}
{% if len(errors) > 0 %}
<ul class="errors">
{% for _, err := range errors %}
<li>{%s err %}</li>
{% endfor %}
</ul>
{% endif %}
{% endstripspace %}{% endfunc %}
{% func FormHiddenField(t validator.FormFieldType) %}{% stripspace %}
<input type="hidden"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}"
value="{%s t.Value %}">
{% endstripspace %}{% endfunc %}
{% func FormTextField(t validator.FormFieldType) %}{% stripspace %}
<input type="text"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}"
value="{%s t.Value %}"
{% if !t.Optional %}required{% endif %}>
{% endstripspace %}{% endfunc %}
{% func FormEmailField(t validator.FormFieldType) %}{% stripspace %}
<input type="email"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}"
value="{%s t.Value %}"
{% if !t.Optional %}required{% endif %}>
{% endstripspace %}{% endfunc %}
{% func FormSearchField(t validator.FormFieldType) %}{% stripspace %}
<input type="search"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}"
value="{%s t.Value %}"
{% if !t.Optional %}required{% endif %}>
{% endstripspace %}{% endfunc %}
{% func FormPasswordField(t validator.FormFieldType) %}{% stripspace %}
<input type="password"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}"
value="{%s t.Value %}"
{% if !t.Optional %}required{% endif %}>
{% endstripspace %}{% endfunc %}
{% func FormCheckboxField(t validator.FormFieldType) %}{% stripspace %}
<input type="checkbox"
name="{%s t.Name %}"
value="{%s t.Value %}"> {%s t.Placeholder %}
{% endstripspace %}{% endfunc %}
{% func FormDateField(t validator.FormFieldType) %}{% stripspace %}
<input type="date"
name="{%s t.Name %}"
placeholder="{%s t.Placeholder %}{%s " " %}({% if !t.ExcludeYear %}YYYY{% endif %}{% if !t.ExcludeYear && !t.ExcludeMonth %}-{% endif %}{% if !t.ExcludeMonth %}MM{% endif %}{% if !t.ExcludeYear && !t.ExcludeMonth && !t.ExcludeDay %}-{% endif %}{% if !t.ExcludeDay %}DD{% endif %})"
value="{%s t.Value %}"
{% if !t.Optional %}required{% endif %}>
{% endstripspace %}{% endfunc %}
{% func CreatedBy(u *database.User) %}{% stripspace %}
{% if nil != u.FirstName && nil != u.Surname %}
{%s *u.FirstName + " " + *u.Surname %}
{% elseif nil != u.FirstName %}
{%s *u.FirstName %}
{% elseif nil != u.Surname %}
{%s *u.Surname %}
{% else %}
{%s u.Email %}
{% endif %}
{% endstripspace %}{% endfunc %}
routeLogin.go
(validator.NewFormValidator
I wrote myself. Kind of works like Symfony's Form handler)
func routeLogin(c context.Context) {
if userID, _ := c.Session().GetInt("user_id"); userID > 0 {
c.Redirect("/")
return
}
locale := c.Values().Get("locale").(languages.Language)
form := validator.NewFormValidator(forms.NewLoginForm(locale.ID), locale)
form.HandleFromRequest(c)
if form.IsSubmitted() {
if form.IsValid() {
// LOGIC...
}
} else {
if referer := c.Request().Referer(); "" != referer {
// LOGIC...
}
}
qtpls.WritePageTemplate(c, &qtpls.LoginPage{
BasePage: defaultBasePage(c),
Context: c,
Form: form.GenerateForm(),
FormErrors: form.Errors(),
})
}
helpers.go
func defaultBasePage(c context.Context) qtpls.BasePage {
return qtpls.BasePage{
IsLoggedIn: (nil != c.Values().Get("user")),
LocaleID: c.Values().Get("locale").(languages.Language).ID,
AdminUrl: &globals.AdminURL,
}
}
It's a fast/stripped paste, so forgive me if there are broken parts :)
I'll add some more template engines in the next weeks. Please stand by.
@allendar which is the part that makes it work with siris?
You make a subpackage (e.a. qtpls
), put your .qtpl
files there and through qtpls.WritePageTemplate(c, ...)
the QuickTemplate parser writes to Siris' context responseWriter. When you run qtc
that comes with QuickTemplate it will Gogenerate the .qtpl
templates into .go
files that deliver a writing interface for super fast buffered output.
@Allendar are you able to create a example for quicktemplate and create a PullRequest?
@Dexus I'll submit a PullRequest somewhere in the weekend. Which example level should this be in? Or maybe make a new one called something like best_practice
or optimal
?
Use examples/best_practice/views
think that would match it.
When I started with Iris last year I used the Django templates for quite some time. Later realizing how terribly slow these rendering engines are I moved to https://github.com/valyala/quicktemplate. But https://github.com/shiyanhui/hero also looks amazing.
Instead of providing examples that might be easy to start with it might be better to instantly set people on the right path and offer fast solutions.
On some Django templates I got speeds that just are as terrible as building a PHP website. Since I use QuickTemplate I get 38k+ req/s.