I've recently swapped out datastores and as a side-effect have had to change a struct field from template.HTML to string to be compatible with the marshaller/DB driver. This field, RenderedDesc, contains rendered HTML as passed through russross/blackfriday.
Previously I could just pass the whole struct into the template "as is" and call {{ .RenderedDesc }} in the template.
Because it's now a string, I've added a filter to convert it back on template render:
templates.go
func RenderUnsafe(s string) template.HTML {
return template.HTML(s)
}
template.FuncMap{
...
"unsafe": RenderUnsafe,
}
_content.tmpl
...
<div class="detail">
{{ .RenderedDesc | unsafe }}
</div>
...
Is there a better way to achieve this without having to use a filter at the template level? Short of re-writing marshalling logic from my DB driver (not on the cards) it looks like this is the simplest way to "store" strings but render raw HTML.
IMHO, the right way to do this is using a filter, like you are already doing. There are more ways to achieve the same, one of them is using tags and converting the struct in to a map[string]Interface{}. Because map fields can be reached in the same way that structs, your templates will remain unmodified.
Show me the code (playground):
package main
import (
"html/template"
"os"
"reflect"
)
var templates = template.Must(template.New("tmp").Parse(`
<html>
<head>
</head>
<body>
<h1>Hello</h1>
<div class="content">
Usafe Content = {{.Content}}
Safe Content = {{.Safe}}
Bool = {{.Bool}}
Num = {{.Num}}
Nested.Num = {{.Nested.Num}}
Nested.Bool = {{.Nested.Bool}}
</div>
</body>
</html>
`))
func asUnsafeMap(any interface{}) map[string]interface{} {
v := reflect.ValueOf(any)
if v.Kind() != reflect.Struct {
panic("asUnsafeMap invoked with a non struct parameter")
}
m := map[string]interface{}{}
for i := 0; i < v.NumField(); i++ {
value := v.Field(i)
if !value.CanInterface() {
continue
}
ftype := v.Type().Field(i)
if ftype.Tag.Get("unsafe") == "html" {
m[ftype.Name] = template.HTML(value.String())
} else {
m[ftype.Name] = value.Interface()
}
}
return m
}
func main() {
templates.ExecuteTemplate(os.Stdout, "tmp", asUnsafeMap(struct {
Content string `unsafe:"html"`
Safe string
Bool bool
Num int
Nested struct {
Num int
Bool bool
}
}{
Content: "<h2>Lol</h2>",
Safe: "<h2>Lol</h2>",
Bool: true,
Num: 10,
Nested: struct {
Num int
Bool bool
}{
Num: 9,
Bool: true,
},
}))
}
Output:
<html>
<head>
</head>
<body>
<h1>Hello</h1>
<div class="content">
Usafe Content = <h2>Lol</h2>
Safe Content = <h2>Lol</h2>
Bool = true
Num = 10
Nested.Num = 9
Nested.Bool = true
</div>
</body>
</html>
Note: the previous code doesn't work with nested structures, but it will be easy to add support for them. Also, every field tagged as unsafe will be treated as string.
Related
I am sending password reset link to my users using cognito CustomMessage_ForgotPassword event. Here is my handler code.
func handler(ctx context.Context, event *events.CognitoEventUserPoolsCustomMessage) (*events.CognitoEventUserPoolsCustomMessage,error)
{
resetURL := fmt.Sprintf("%s/login/recover", os.Getenv("DOMAIN_URL"))
var customMessageForgotPassword = fmt.Sprintf(
`<style>
p {
display: block;
}
</style>
<div tabindex="-1"><p>Hello,</p>
<p> Click on link below to reset your password.</p>
<p> Reset Password </p>
<p>Thanks</p>
</div>`,
resetURL,
event.Request.CodeParameter, event.Request.UserAttributes["Email"])
if event.TriggerSource == "CustomMessage_ForgotPassword" {
event.Response.EmailMessage = customMessageForgotPassword
event.Response.EmailSubject = "Reset Your Password"
}
return event, nil
}
Here is the definition of CognitoEventUserPoolsCustomMessageRequest in cognito.go file.
type CognitoEventUserPoolsCustomMessage struct {
CognitoEventUserPoolsHeader
Request CognitoEventUserPoolsCustomMessageRequest `json:"request"`
Response CognitoEventUserPoolsCustomMessageResponse `json:"response"`
}
type CognitoEventUserPoolsCustomMessageRequest struct {
UserAttributes map[string]interface{} `json:"userAttributes"`
CodeParameter string `json:"codeParameter"`
UsernameParameter string `json:"usernameParameter"`
ClientMetadata map[string]string `json:"clientMetadata"`
}
In email I am getting Reset password url parameters like Domain_URL/login/recover?code=13578&email=%!s()
Tried with parameters like UsernameParameter and some other solutions. But always getting nil here.
Any solution to this problem?
I'm trying to learn Golang. I want to do simply sending data to view. But data is does not reaching to main.gohtml. I could not understand the reason. If I print the data out of the define it works. But If I want to print data in the define "content" (goes to main.gohtml), data is coming empty.
define "title" part is working. Just I cant send data with variable. If I delete {{.text}} part and write something, it works.
main.go file
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseGlob("template/*.gohtml"))
}
func main() {
http.HandleFunc("/about", fabout)
http.ListenAndServe(":80", nil)
}
func fabout(w http.ResponseWriter, r *http.Request) {
values, isset := r.URL.Query()["text"]
var value string
if isset == true {
value = values[0]
} else {
value = ""
}
data := map[string]interface{}{
"text": value,
}
tpl.ExecuteTemplate(w, "about.gohtml", data)
}
about.gohtml
{{template "main"}}
{{define "title"}}About me{{end}} //this is working
{{define "content"}}{{.text}}{{end}} //this is not working
{{.text}} //this is working
main.gohtml
{{define "main"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{template "title"}}</title>
</head>
<body>
<ul>
<li>Page 1</li>
<li>Page 2</li>
<li>Page 3</li>
<li>Page 4</li>
<li>Page 5</li>
</ul>
<div style="padding:100px 0;">{{template "content"}}</div>
<footer>
this is footer
</footer>
</body>
</html>
{{end}}
When calling a template you need to pass in any necessary data. The syntax from the docs is:
{{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.
So a simple way to fix this is to pass . in when calling main:
{{template "main" .}
and the same when calling content:
{{template "content" .}}
And finally content can use the value:
{{define "content"}}{{.text}}{{end}}
note: Using the argument . passes all data; you could also just pass .text into main (and then use . when calling content and within content ({{.}}).
For a simplified working example see this playground.
My Observation is :
here in this below function , variable value is empty. there I added line (value = "Okay") for testing and is found working.
func fabout(w http.ResponseWriter, r *http.Request) {
values, isset := r.URL.Query()["text"]
var value string
if isset == true {
value = values[0]
} else {
value = ""
}
value = "Okay" // THIS LINE ADDED EXTRA
data := map[string]interface{}{
"text": value,
}
tpl.ExecuteTemplate(w, "about.gohtml", data)
}
I use "text/template" module.
I have struct like this to parse XML from Blogger
type Media struct {
ThumbnailUrl string `xml:"url,attr"`
}
type Entry struct {
ID string `xml:"id"`
Published Date `xml:"published"`
Updated Date `xml:"updated"`
Draft Draft `xml:"control>draft"`
Title string `xml:"title"`
Content string `xml:"content"`
Tags Tags `xml:"category"`
Author Author `xml:"author"`
Media Media `xml:"thumbnail"`
Extra string
}
Then I create Go Template like this
[image]
src = "{{ replace .Media.ThumbnailUrl 's72-c' 's1600' }}"
link = ""
thumblink = "{{ .Media.ThumbnailUrl }}"
alt = ""
title = ""
author = ""
license = ""
licenseLink = ""
The replace function not defined. I want to replace URL from {{ .Media.ThumbnailUrl }}
For example:
from this URL
https://2.bp.blogspot.com/-DEeRanrBa6s/WGWGwA2qW5I/AAAAAAAADg4/feGUc-g9rXc9B7hXpKr0ecG9UOMXU3_VQCK4B/s72-c/pemrograman%2Bjavascript%2B-%2Bpetanikode.png
To this URL
https://2.bp.blogspot.com/-DEeRanrBa6s/WGWGwA2qW5I/AAAAAAAADg4/feGUc-g9rXc9B7hXpKr0ecG9UOMXU3_VQCK4B/s1600/pemrograman%2Bjavascript%2B-%2Bpetanikode.png
You can write a helper view function like this
func replace(input, from,to string) string {
return strings.Replace(input,from,to, -1)
}
funcMap = template.FuncMap{
"replace": replace,
}
template := template.New("").Funcs(internalFuncMap)
and use the template to render the view.
code ref links
https://github.com/sairam/kinli/blob/master/template_funcs.go#L57-L59
https://github.com/sairam/kinli/blob/master/templates.go#L48
I'm trying to do validation on my form struct in a method that returns a bool, but I keep getting false even when it should be returning true..
If you look towards the end of the Validate method, you'll see I write validated := len(this.Errors) == 0 which should be making "validated" either true or false based on whether the Errors map has items or not, and then I return validated.
When I fill out my form accurately, there should be no errors yet I still get false when I should be getting true.
Can someone explain? Is this not how Go works?
form.go:
package models
import (
"../config"
"../util"
)
type Form struct {
Name string
Email string
Phone string
Message string
Thanks string
ErrorHandler
}
func (this *Form) Validate() bool {
this.Errors = make(map[string]string)
matched := util.MatchRegexp(".+#.+\\..+", this.Email)
if !util.IsEmpty(this.Email) {
if matched == false {
this.Errors["Email"] = config.EMAIL_INVALID
}
} else {
this.Errors["Email"] = config.EMAIL_EMPTY
}
if util.IsEmpty(this.Name) {
this.Errors["Name"] = config.NAME_EMPTY
}
if util.IsEmpty(this.Phone) {
this.Errors["Phone"] = config.PHONE_EMPTY
}
if util.IsEmpty(this.Message) {
this.Errors["Message"] = config.MESSAGE_EMPTY
}
validated := len(this.Errors) == 0
if validated {
this.Thanks = config.THANK_YOU
}
return validated
}
errorhandler.go:
package models
type ErrorHandler struct {
Errors map[string]string
}
func (this *ErrorHandler) HandleErr(err string) {
this.Errors = make(map[string]string)
this.Errors["Error"] = err
}
And this is where I try to call the Validate method -- in a function in my controller:
form := &models.Form{
Name: r.FormValue("name"),
Email: r.FormValue("email"),
Phone: r.FormValue("phone"),
Message: r.FormValue("message")}
if form.Validate() {
// This never runs because 'form.Validate()' is always false
}
I don't think the util.IsEmpty() is the culprit here.. just checks if the string is empty:
func IsEmpty(str string) bool {
return strings.TrimSpace(str) == ""
}
Any help would be appreciated!
It's best to debug this kind of problem with a log statement like:
log.Printf("form: %v", form)
before calling validate, so it's clear what the input data looks like.
Greetings, Philip
in the following model:
package models
import "github.com/astaxie/beego/orm"
type Movie struct {
Id int `orm:"pk; auto; column(id)"; form: "-"`
Title string `orm:"unique; column(title)"; form: "title, text, title:`
Plot string `orm:"column(plot)"; form: "plot, text, plot:"`
ImdbID string `orm:"column(imdb_id)"; form: "imdb_id, text, imdb_id:"`
Actors string `orm:"column(actors)"; form: "actors, text, actors:"`
Runtime string `orm:"column(runtime)"; form: "runtime, text, runtime:"`
Year string `orm:"column(year)"; form: "year, text, year:"`
Genre *Genre `orm:"rel(fk); on_delete(do_nothing)"`
Cover string `orm:"column(cover)"; form: "cover, text, cover:"`
Status int `orm:"column(status)"; form: "status, int, status:"`
}
func (a *Movie) TableName() string {
return "app_movie"
}
func init() {
orm.RegisterModel(new(Movie))
}
I would like to refer to: the genre model, which looks like:
package models
import "github.com/astaxie/beego/orm"
type Genre struct {
Id int `orm:"pk; auto; column(id)"; form: "-"`
Title string `orm:"unique; column(title)"; form: "title, text, title:`
Status int `orm:"column(status)"; form: "status, int, status:"`
}
func (a *Genre) TableName() string {
return "app_genre"
}
func init() {
orm.RegisterModel(new(Genre))
}
For information it goes to an sqlite db with data (DB first).
In the controller i iterate over a list of movies and print out: movie.Genre.Title but its empty. If I watch on movie.Genre I get: {3 0}
What to do to get to the title and the ID? Or. what am I doing wrong?
Thanks for your help!
num, err := qs
.Limit(4)
.Filter("status", true)
.RelatedSel()
.Exclude("useritem__userid", 1)
.OrderBy("-id")
.All(&movies)
all works fine. :) .RelatedSel()