CloudyKit / jet

Jet template engine
Apache License 2.0
1.26k stars 106 forks source link

Importing Jet Template does not bring Variables #107

Open JesseRyan opened 6 years ago

JesseRyan commented 6 years ago

Consider the following:

Params.jet ->
{{ param1 := 1 }}
{{ param2 := 2 }}
{{ param3 := 3 }}
{{ param4 := 4 }}

then import Params.jet in another template.

SomeJetTemplate.jet ->
{{ import "Params.jet" }}
{{ "This is a string needing to use parameters : " + param1 }}

This throws a block level scope failure that param1 does not exist in the scope of the block... Is there a way to load parameter configurations like this into other jet templates? Seems like this should work...

I do of course understand that parameters can be added to vars and imported into templates from compiled go code. However, we would like to remain dynamic and maintain the essence of Jet templates in that we could change the text i.e. the parameters on the fly without having to recompile go code.

annismckenzie commented 6 years ago

Can you try include instead of import?

Import is used for making block definitions available for use, include evaluates the templates.

On Oct 17, 2018, at 10:25 PM, JesseRyan notifications@github.com wrote:

Consider the following:

Params.jet -> {{ param1 := 1 }} {{ param2 := 2 }} {{ param3 := 3 }} {{ param4 := 4 }} then import Params.jet in another template.

SomeJetTemplate.jet -> {{ import "Params.jet" }} {{ "This is a string needing to use parameters : " + param1 }} This throws a block level scope failure that param1 does not exist in the scope of the block... Is there a way to load parameter configurations like this into other jet templates? Seems like this should work...

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

annismckenzie commented 6 years ago

Did that help?

JesseRyan commented 6 years ago

Hey thank you for attention again,

Unfortunately this did not work. Is this intended to work? Perhaps I need to test this in a block for evaluation? Right now its just included in the template.

Do you have a working example? It would be really great if this would work. JET is already heroic by the way.

voidhaze commented 5 years ago

having the same issue, +1 for feature request.

voidhaze commented 5 years ago

found a work around; {{ var = value }} seems to declare with global scope

mchampaneri commented 5 years ago

You have to pass data as third parameter like this,

 {{ include "Params.jet" .  }} // Here '.' means value of data passed to scope
 {{ include "Params.jet" [some variable]  }} // Here '.' means value of data passed to scope

Example,

Main Html Template https://github.com/mchampaneri/MarchCMS/blob/master/themes/shortshot/pages/main.html

Partial Template https://github.com/mchampaneri/MarchCMS/blob/master/themes/shortshot/pages/partials/singlePost.html

mchampaneri commented 5 years ago

found a work around; {{ var = value }} seems to declare with global scope

You don't need to declare in global scope.

sauerbraten commented 4 years ago

found a work around; {{ var = value }} seems to declare with global scope

That was actually a bug and fixed in https://github.com/CloudyKit/jet/commit/16fe51250caa1c7e875d58b2dcd01e33e85b2e0e, so this won't work as a "workaround" anymore.

I'll look into importing variables as well was blocks, but I'm not sure it's something you'd want. Would you expect a variable "foo" from an imported template to overwrite a variable "foo" in your current template scope?

jan-herout commented 3 years ago

@sauerbraten actually, in some cases this would be quite handy. Imagine you have set of templates that should be able to derive the same set of variables/attributes from what was provided to them.

For example:

In this situation, it would be most useful to be able to include a template which contains the common logic and provides metadata to other templates. Imagine it as a subroutine.

Workaround:

This solution is "good from far", but far from good, I am afraid. But in my case it works.

Please note that I am posting this only to show that the case for this functionality is probably there, not to offer any solution. For my case, it would be best if this was provided "out of the box", but I am afraid my knowledge of Jet internals is insufficcient for such a task.

Example use case

Please not that the example uses custom delimiters.

Consumer of the "common" logic

// include "globals"
<%- include "../COMMON/trf_commons.jet" map("database", o.GetTable().Database) %>

// later in the template .... access metadata prepared by trf_commons.jet
<%- pmrootdir := stashGet("pmRootDir")  %>

Provider of the "common" logic" (trf_commons.jet)

PLease note that this is a contrived and incomplete example. But it shows the idea.

<%- stashReset(false) %>
<%- database  := upper(.["database"])      %>
<%- if 
        database == "EP_MREP"       
     || database == "EP_MREP_WRK"
     || database == "EP_MREPADJ"
     || database == "EP_IPTV"
     || database == "EP_IPTV_WRK"           %>
     <%- engineId   = "21"                  %>
     <%- pmRootDir  = "PMRootDir_E21"       %>
     <%- streamINITName  = "START_021"      %>
     <%- streamFINALName = "FINAL_021"      %>
     <%- jobCategory     = "COMMAND_E21"    %>
<% end %>

<%- stashSet("engineId",     engineId)   %>
<%- stashSet("pmRootDir",    pmRootDir)  %>
<%- stashSet("auditRunID",   auditRunID) %>
<%- stashSet("pdc.streamINITName" , streamINITName  )  %>
<%- stashSet("pdc.streamFINALName", streamFINALName )  %>
<%- stashSet("pdc.jobCategory"    , jobCategory     )  %>

Snippet of code

// stashReset creates an empty global stash. The global stash is mutex protected map[string]string, which can
// later on be accessed via stashSet and stashGet functions.
// The function takes one oprional parameter. If the value provided is string "true", then stashGet WILL panic
// if you attempt to access key which was not yet stored using stashSet.
func stashReset(a jet.Arguments) reflect.Value {
    a.RequireNumOfArguments("stashReset", 0, 1)
    canPanic := strings.ToLower(jetGetParam(a, 0)) == "true"
    globalStash.m.Lock()
    globalStash.stash = make(map[string]string)
    globalStash.canPanic = canPanic
    globalStash.m.Unlock()
    return reflect.ValueOf("")
}

// stashSet stores a string value in the stash, and returns an empty string.
func stashSet(a jet.Arguments) reflect.Value {
    a.RequireNumOfArguments("stashSet", 2, 2)
    key := jetGetParam(a, 0)
    val := jetGetParam(a, 1)
    globalStash.m.Lock()
    globalStash.stash[key] = val
    globalStash.m.Unlock()
    return reflect.ValueOf("")
}

// stashGet retrieves string from global stash.
func stashGet(a jet.Arguments) reflect.Value {
    a.RequireNumOfArguments("stashGet", 1, 1)
    key := jetGetParam(a, 0)
    globalStash.m.Lock()
    val, ok := globalStash.stash[key]
    globalStash.m.Unlock()
    if !ok {
        if globalStash.canPanic {
            a.Panicf("stashGet: key not in stash: %s", key)
        }
    }
    r := reflect.ValueOf(val)
    return r
}
sauerbraten commented 3 years ago

It sounds like you could use extends for your use case: https://github.com/CloudyKit/jet/blob/master/docs/syntax.md#extends

Have a base template defining all the "global" vars you want, then make different "consuming" templates that extend from it. Since code outside of blocks doesn't run in the "consumer" (extending) templates, you'd have to define something like a main block and yield that in the base, as described in the docs.

Would this not solve the problem you're having?