Comments (11)
I tried setting up a global variable called FuncMaps on my controller and then putting it in the render options FuncMap like:
m.Use(render.Renderer(render.Options{
Directory: "templates",
Layout: "layout",
Extensions: []string{".tmpl", ".html"},
Delims: render.Delims{"{{", "}}"},
Charset: "UTF-8",
IndentJSON: true,
HTMLContentType: "text/html",
Funcs: []template.FuncMap{
{
blogCtlr.FuncMaps
}
}
}))
This works for some things, but there are times when you need to compare it against a specific instance, from a list, etc.
var (
FuncMaps = template.FuncMap{
"formatDate": func(dt time.Time) string {
tlayout := "01/02/2006 3:04 PM"
Local, _ := time.LoadLocation("US/Central")
return dt.In(Local).Format(tlayout)
},
"showCommentsLink": func(c blog.Comments) bool {
return len(c.Approved) > 0 || len(c.Unapproved) > 0
},
}
)
func EditPost(ctx *web.Context, r render.Render) {
str_id := ctx.FormValue("postid")
postID, err := strconv.Atoi(str_id)
if err != nil {
ctx.Redirect(http.StatusFound, "/blog")
return
}
var post blog.Post
if postID > 0 {
post = blog.Post{ID: postID}
if err := post.Get(); err != nil {
ctx.Redirect(http.StatusFound, "/blog")
return
}
} else {
post = blog.Post{ID: 0}
}
...
FuncMap["isUser"] = func(uid int) bool {
return uid == post.UserID
}
FuncMap["hasCategory"] = func(cid int) bool {
for _, cat := range post.Categories {
if cid == cat.ID {
return true
}
}
return false
}
...
r.HTML(http.StatusFound, "blog/edit.html", post)
}
...
<div class="form-group">
<label for="userid" class="col-lg-2 control-label">Author</label>
<div class="col-lg-10">
<select name="userid" id="userid">
<option value="0">Choose an Author</option>
{{range .AllUsers}}
<option value="{{.ID}}" {{if isUser .ID }}selected="selected"{{end}}>{{.Fname}} {{.Lname}}</option>
{{end}}
</select>
</div>
</div>
...
So obviously this approach doesn't seem to work either.
from render.
@codegangsta : Any ideas or suggestions? Thanks man.
from render.
This is unfortunately a pain point in the Go templating system. The template loader wants to know all of the functions in the funcmap before the templates are compiled. This is an issue as we precompile our templates for performance reasons.
from render.
Thanks for the reply @codegangsta . I agree that it is a pain point, but at the same time it's a very useful thing, especially when doing things with dynamic or semi-dynamic views. I do understand the reasoning for and why things are precompiled, but it doesn't necessarily work in situations where you need to check things prior to rendering/displaying a view. The reasoning for dynamic function map behavior is that it allows me to write components to behave differently from one view to another. For example, I have a component that paginates my data and within my controller I get the data, calculate the number of pages, etc.
The project I am working on really needs this functionality and I am really struggling to figure out a way to make this achievable. One idea I had was to figure out a way to skip the regular compile phase and to somehow compile/render it at run-time. Another would be to publicly expose all rendering methods.
from render.
@unrolled : Do you have any ideas or suggestions? I would try to add in the functionality I need myself, but I am really struggling with it so that's why I am asking for some help.
from render.
Hmmm, let me ponder this for a day or two. I understand what you're attempting to do... but doing it in a properly and elegant fashion may be challenging.
from render.
@mickelsonm Well unfortunately I don't have a solid solution for this. My only suggestion would be precompiling what you expect. In your example above you have FuncMaps for isUser, and hasCategory... I would just link the results to a map and pass the result to your template:
func EditPost(ctx *web.Context, r render.Render) {
str_id := ctx.FormValue("postid")
postID, err := strconv.Atoi(str_id)
if err != nil {
ctx.Redirect(http.StatusFound, "/blog")
return
}
var post blog.Post
if postID > 0 {
post = blog.Post{ID: postID}
if err := post.Get(); err != nil {
ctx.Redirect(http.StatusFound, "/blog")
return
}
} else {
post = blog.Post{ID: 0}
}
data := make(map[string]interface{})
data["post"] = post
...
data["isUser"] = (uid == post.UserID)
data["hasCategory"] = func(cid int) bool {
for _, cat := range post.Categories {
if cid == cat.ID {
return true
}
}
return false
}(cid)
...
r.HTML(http.StatusFound, "blog/edit.html", data)
}
...
<div class="form-group">
<label for="userid" class="col-lg-2 control-label">Author</label>
<div class="col-lg-10">
<select name="userid" id="userid">
<option value="0">Choose an Author</option>
{{range .post.AllUsers}}
<option value="{{.post.ID}}" {{if .isUser }}selected="selected"{{end}}>{{.post.Fname}} {{.post.Lname}}</option>
{{end}}
</select>
</div>
</div>
...
from render.
@unrolled : Thanks for taking the time to review this. I'll try your approach out soon.
from render.
So I had a chance to dive into this approach some and have realized that this approach leads to a scoping problem. Note how I am using 'formatDate' and 'showCommentsLink':
Here's my controller/handler:
func Index(rw http.ResponseWriter, r *http.Request, ren render.Render) {
data := make(map[string]interface{})
data["PageTitle"] = "Blog"
data["Categories"], _ = blog.BlogCategory{}.GetAll()
data["Posts"], _ = blog.Post{}.GetAll()
data["Comments"], _ = blog.Comment{}.GetAll()
data["formatDate"] = func(dt time.Time) string {
tlayout := "01/02/2006 3:04 PM"
Local, _ := time.LoadLocation("US/Central")
return dt.In(Local).Format(tlayout)
}
data["showCommentsLink"] = func(c blog.Comments) bool {
return len(c.Approved) > 0 || len(c.Unapproved) > 0
}
ren.HTML(http.StatusOK, "blog/index", data)
}
Here's my template:
<tbody>
{{range $post := .Posts}}
<tr>
<td>{{$post.Title}}</td>
<td>{{.formatDate $post.Created}}</td>
<td>{{if $post.Published.IsZero }}Not Published {{else}} {{.formatDate $post.Published}}{{end}}</td>
<td>{{len .Comments.Approved}}</td>
<td>{{len .Comments.Unapproved}}</td>
<td>
<div class="btn-group">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
Action
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="/blog/posts/{{$post.ID}}">Edit</a></li>
<li><a href="#" class="delete" data-id="{{$post.ID}}">Delete</a></li>
{{if .showCommentsLink $post.Comments}}
<li><a href="/blog/postcomments/{{$post.ID}}">View Comments</a></li>
{{end}}
</ul>
</div>
</td>
</tr>
{{end}}
</tbody>
So how do I say to use my function map and not have it think that formatDate and showCommentsLink are fields that are part of my Post object?
from render.
Ahh I see what you're doing there. My implementation was assuming you were dealing with a single post. Not iterating over multiple... With a single post I was insinuating you run any functions against the post before passing it to the template. This would have avoided adding the functions to the FuncMap.
Sorry for the confusion.
from render.
I'll go ahead and close this, seeing the pre-rendering of templates makes this impossible to do with the current code base. The lesson I have learned here is that you need to be extremely careful when using function maps in your go templates. Thanks for the help guys!
from render.
Related Issues (20)
- How to change content header? HOT 1
- Relative URL in template HOT 3
- Multiple Extension Points in Layouts HOT 4
- json encoding remove fields HOT 2
- Transfer "HTMLContentType" from Options to HTMLOptions
- Is it possible to have multiple layouts? HOT 5
- Not use a template for specific view HOT 1
- Value not found for type render.Render HOT 4
- How to show multi variables in tmpl file? HOT 2
- Define sections for template parsing. HOT 2
- yield'ing some specific part
- Calling function from template
- 404 error on static directory HOT 1
- Default options inconsistent with those presented in the README
- Possible Memory Leak
- Missing return at end of function HOT 2
- need an other "yield"
- where is the template syntax document ? HOT 1
- Layout reloading every redirection HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from render.