Code Monkey home page Code Monkey logo

Comments (11)

mickelsonm avatar mickelsonm commented on September 6, 2024

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.

mickelsonm avatar mickelsonm commented on September 6, 2024

@codegangsta : Any ideas or suggestions? Thanks man.

from render.

codegangsta avatar codegangsta commented on September 6, 2024

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.

mickelsonm avatar mickelsonm commented on September 6, 2024

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.

mickelsonm avatar mickelsonm commented on September 6, 2024

@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.

unrolled avatar unrolled commented on September 6, 2024

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.

unrolled avatar unrolled commented on September 6, 2024

@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.

mickelsonm avatar mickelsonm commented on September 6, 2024

@unrolled : Thanks for taking the time to review this. I'll try your approach out soon.

from render.

mickelsonm avatar mickelsonm commented on September 6, 2024

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.

unrolled avatar unrolled commented on September 6, 2024

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.

mickelsonm avatar mickelsonm commented on September 6, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.