Repository: go
Issue: text/template, html/template: add ExecuteFuncs
Labels: Proposal,Proposal-Accepted

This proposal is about providing a way to pass a `template.FuncMap` at template execution overriding specific functions within the template.

When executing a template using one of new methods, then the provided `template.FuncMap` will be checked first.

# Summary

Reasons:

* Passing values to templates can only be achieved using the `data any` parameter and this is suboptimal for contextual rendering elements such as web nonces like `csrf`, or translation functions.
* This is especially problematic when using sub-templates called from within template rendering. The template calling syntax only allows setting a single variable as its data, making passing this contextual rendering functions difficult.
* The current suggestion is to use `Clone()`, `Funcs()` and then `Execute()` but this is slow and optimizing this appears very difficult. (#38114) It would require significant changes throughout the code and even if parent templates could be changed to allow inheritance, It would requires cloning every template that could be called in advance. 

Advantages:

* The proposal is easy to implement (see #54432)
* The additional methods are simple to use and represent a very small API change.
* The change will allow significant simplification of templates in web services.

# Detailed rationale

Sometimes data needs to be rendered differently based on the context it is rendered in.

The two most common examples [anti-CSRF tokens](https://en.wikipedia.org/wiki/Cross-site_request_forgery) and locales.

Although currently these can be added to the data struct provided when rendering - these are not strictly data and it significantly complicates rendering especially when considering subtemplating.

# Proposed API

I propose that the following methods are added to `text/template.Template` and to `html/template.Template`

`text/template`:

```go
func (t *Template) ExecuteFuncMap(wr io.Writer, data any, funcs FuncMap) error { ... }

func (t *Template) ExecuteTemplateFuncMap(wr io.Writer, name string, data any, funcs FuncMap) error { ... }
```

`html/template`:

```go
func (t *Template) ExecuteFuncMap(wr io.Writer, data any, funcs FuncMap) error { ... }

func (t *Template) ExecuteTemplateFuncMap(wr io.Writer, name string, data interface{}, funcs FuncMap) error {
```

Calling these methods would execute the template as per `Execute` and `ExecuteTemplate` with the only difference being that when a template calls a function the provided `funcs template.FuncMap` would be checked first. In this way `funcs` can be considered a map of function overrides for template execution and acts similarly to the calling the `Funcs(...)` method on the template but would also override functions in templates called by the current executing template. 

# Examples

## Current Status
Let us assume we have a function with the following signature:

```go
func (l *Locale) Tr(key string, args ...any) string { ... }
```

In this example this function takes a `key` and provides the translated value for it.

Clearly, this cannot be compiled into shared templates as the locale will be different on a per request basis.

So assuming we don't want to have multiple compiled templates per locale we will need to pass this in as data. Let's say as `.Tr`.

This leads to:

```template
...
{{.Tr "key" args}}
...
```

But... what about when ranging over a set of items? Now, `.` will refer to an item in the range of items so we need use `$`...

```template
{{range .Items}}
   ...
   {{$.Tr "key" .}}
   ...
{{end}}
```

If we forget we will get a runtime error. So we won't know that it's a problem until it's run.

But... what if we want to reuse that range block as a subtemplate or even just extract it out because it is becoming unwieldy? Ideally we'd want to do:

```template
{{range .Items}}
   {{template "render-item" .}}
{{end}}
```

That of course doesn't work because we lose access to `$`. So now we need to do something horrible like:

```
{{range .Items}}
   {{template "render-item" dict "Item" . "Tr" $.Tr}} {{/* or "root" $ */}}
{{end}}
```

and then in our subtemplate refer to `.Item` and `.Tr` (or `.root.Tr`). This means that we need to now know that we're in a subtemplate and every time we call the subtemplate we need to do a similar hack.

Again if we forget to add the correct prefix we'll get a runtime error but only at time of render.

## Why isn't `template.Clone()` `template.Funcs()` `template.Execute()` sufficient?

The problem here is multifactorial, `Clone()` isn't very lightweight and requires locks, `Funcs(...)` also requires locks and every template would need to be cloned and its funcs updated in case when you execute the template it calls another template.

The referenced issue has been around for over 2 years and it is likely that improving the efficiency of this route significantly is not possible without significant refactors.

Instead, the proposed API makes for a clear and simple implementation with a minimal changeset.

## Proposed

The proposed API allows for much simpler templating. The `Tr` function would be passed in within an override `FuncMap` e.g.:

```go
err := tmpl.ExecuteFuncMap(wr, data, template.FuncMap(map[string]any{"Tr": locale.Tr})
```

Then the downstream templates look like:

```template
{{Tr "key" args...}}
{{range .Items}}
    {{Tr "key" .}}
    {{template "item-template" .}}
{{end}}
```

And the `Tr` function is available everywhere without having to determine whether it needs a `.`, a `$` or even a `$.root`. (Or even if it's not been passed in to this subtemplate.) 

## Compatibility

The proposed functionality is completely backwards compatible. 

## Implementation

The proposal is easy to implement and has been implemented as #54432.