Search code examples
gogo-templatesgo-html-templateanonymous-struct

Anonymous struct as pipeline in template


Is there a way to do the following in a html/template?

{{template "mytemplate" struct{Foo1, Foo2 string}{"Bar1", "Bar2"}}}

Actually in the template, like above. Not via a function registered in FuncMap which returns the struct.

I tried it, but Parse panics, see Playground. Maybe just the syntax is wrong?


Solution

  • As noted by others, it's not possible. Templates are parsed at runtime, without the help of the Go compiler. So allowing arbitrary Go syntax would not be feasible (although note that it wouldn't be impossible, as the standard lib contains all the tools to parse Go source text, see packages "prefixed" with go/ in the standard lib). By design philosophy, complex logic should be outside of templates.

    Back to your example:

    struct{Foo1, Foo2 string}{"Bar1", "Bar2"}
    

    This is a struct composite literal and it is not supported in templates, neither when invoking another template nor at other places.

    Invoking another template with a custom "argument" has the following syntax (quoting from text/template: Actions):

    {{template "name" pipeline}}
        The template with the specified name is executed with dot set
        to the value of the pipeline.
    

    TL;DR; A pipeline may be a constant, an expression denoting a field or method of some value (where the method will be called and its return value will be used), it may be a call to some "template-builtin" function or a custom registered function, or a value in a map.

    Where Pipeline is:

    A pipeline is a possibly chained sequence of "commands". A command is a simple value (argument) or a function or method call, possibly with multiple arguments:

    Argument
      The result is the value of evaluating the argument.
    .Method [Argument...]
      The method can be alone or the last element of a chain but,
      unlike methods in the middle of a chain, it can take arguments.
      The result is the value of calling the method with the
      arguments:
          dot.Method(Argument1, etc.)
    functionName [Argument...]
      The result is the value of calling the function associated
      with the name:
          function(Argument1, etc.)
      Functions and function names are described below.
    

    And an Argument is:

    An argument is a simple value, denoted by one of the following.

    - A boolean, string, character, integer, floating-point, imaginary
      or complex constant in Go syntax. These behave like Go's untyped
      constants. Note that, as in Go, whether a large integer constant
      overflows when assigned or passed to a function can depend on whether
      the host machine's ints are 32 or 64 bits.
    - The keyword nil, representing an untyped Go nil.
    - The character '.' (period):
      .
      The result is the value of dot.
    - A variable name, which is a (possibly empty) alphanumeric string
      preceded by a dollar sign, such as
      $piOver2
      or
      $
      The result is the value of the variable.
      Variables are described below.
    - The name of a field of the data, which must be a struct, preceded
      by a period, such as
      .Field
      The result is the value of the field. Field invocations may be
      chained:
        .Field1.Field2
      Fields can also be evaluated on variables, including chaining:
        $x.Field1.Field2
    - The name of a key of the data, which must be a map, preceded
      by a period, such as
      .Key
      The result is the map element value indexed by the key.
      Key invocations may be chained and combined with fields to any
      depth:
        .Field1.Key1.Field2.Key2
      Although the key must be an alphanumeric identifier, unlike with
      field names they do not need to start with an upper case letter.
      Keys can also be evaluated on variables, including chaining:
        $x.key1.key2
    - The name of a niladic method of the data, preceded by a period,
      such as
      .Method
      The result is the value of invoking the method with dot as the
      receiver, dot.Method(). Such a method must have one return value (of
      any type) or two return values, the second of which is an error.
      If it has two and the returned error is non-nil, execution terminates
      and an error is returned to the caller as the value of Execute.
      Method invocations may be chained and combined with fields and keys
      to any depth:
        .Field1.Key1.Method1.Field2.Key2.Method2
      Methods can also be evaluated on variables, including chaining:
        $x.Method1.Field
    - The name of a niladic function, such as
      fun
      The result is the value of invoking the function, fun(). The return
      types and values behave as in methods. Functions and function
      names are described below.
    - A parenthesized instance of one the above, for grouping. The result
      may be accessed by a field or map key invocation.
      print (.F1 arg1) (.F2 arg2)
      (.StructValuedMethod "arg").Field
    

    The proper solution would be to register a custom function that constructs the value you want to pass to the template invocation, as you can see in this related / possible duplicate: Golang pass multiple values from template to template?

    Another, half solution could be to use the builtin print or printf functions to concatenate the values you want to pass, but that would require to split in the other template.