For example, I want to range Fields except the last one element.
Maybe like:
{{range $Field := $.Fields[:len $Field - 1]}}
Do I have some approaches?
Thx!
The builtin template slice function almost does what you need. The missing piece is computing the last index of the new slice. To do that, add an addition function to the template:
func add(a, b int) int {
return a + b
}
Add the function to template before parsing:
t, err := template.New(name).Funcs(template.FuncMap{"add": add}).Parse(text)
Use the function like this:
{{range slice $ 0 (add (len $) -1)}}
{{.}}
{{end}}
playground example.
Related
How can you calculate something inside a html template of go?
For example:
{{ $length := len . }}
<p>The last index of this map is: {{ $length -1 }} </p>
Were the . is a map.
The code {{ $length -1 }} is not working, is there a way to achieve this?
You can't. Templates are not a scripting language. By design philosophy, complex logic should be outside of templates.
Either pass the calculated result as a parameter (preferred / easiest), or register custom functions which you can call during template execution, pass values to them and which may perform calculations and return any values (e.g. return param - 1).
For examples of registering and using custom functions, see:
Golang templates (and passing funcs to template)
How do I access object field by variable in template?
Iterate Go map get index.
The other answers are correct, you can't do it in the template themselves. However, here's a working example of how to use Funcs:
package main
import (
"fmt"
"html/template"
"os"
)
type MyMap map[string]string
func LastMapIndex(args ...interface{}) string {
if m, ok := args[0].(MyMap); ok && len(args) == 1 {
return fmt.Sprintf("%d", len(m) - 1)
}
return ""
}
func main() {
myMap := MyMap{}
myMap["foo"] = "bar"
t := template.New("template test")
t = t.Funcs(template.FuncMap{"LastMapIndex": LastMapIndex})
t = template.Must(t.Parse("Last map index: {{.|LastMapIndex}}\n"))
t.Execute(os.Stdout, myMap)
}
Playground: https://play.golang.org/p/YNchaHc5Spz
You can use a FuncMap like this. Once you define a function within a funcmap, you can use it in the HTML. In your case you could define a MapLength function or something similar that calculates the length of a given map and returns it for you. You can then call it in the template a bit like this:
<p>The last index of this map is: {{ .MapLength . }} </p>
I have a struct, containing two slices with same size, which is to be passed to the template.
type V struct {
Process []string
ProcessType []string
}
I need to iterate the slices in same range. In golang(without a framework) we could do this as follows
//I know all of you know this. Added for better understanding of my problem.
{{range $i, $e := .Process}}
{{.}}
{{index $.ProcessType $i}}
{{end}}
We could apply range on a slice in beego like this
{{range $key, $val := .vm.Process}} //this.Data["vm"] = V (Struct V is passed as vm to the template)
{{$val}}
{{end}}
How to include slice ProcessType in this range too?
How can I slice strings in a template using the text/template package? Of course, something like {{ $myString[0:5] }} is not working.
Define your own slicing function with template.Funcs.
Code:
t.Funcs(template.FuncMap{
"stringSlice": func(s string, i, j int) string {
return s[i:j]
}
})
Template:
{{ stringSlice .MyString 0 5 }}
See also: Template and custom function; panic: function not defined
PS: As #dyoo correctly noted in the comments; this minimal stringSlice function does nothing to prevent you from slicing UTF-8 characters in half. You should probably handle that in a live environment.
Let's say a function takes a slice of strings:
func Join(strs []string) {
...
}
I have a single string:
a := "y'all ain't got the honey nut?"
How can I convert that string into a slice?
You can create a slice of one item using the following convention:
a := "y'all ain't got the honey nut?"
singleItemArray := []string{a}
strings.Join(singleItemArray);
The actual answer to your question is as simple as []string{"string"}, as miltonb said.
But what I wanted to point out is how easy it is to write and use a variadic function in Go, a function with a variable number of arguments.
You can change signature of your function to F(a ...string). Then, a is slice in the function F, and you can call it like F("a") and F("a", "b"). And when you actually have a slice or array, you can pass it to F by calling F(a...).
Not sure if this syntax fits your job, but I wanted to let you know about it as an option.
The question as phrased actually references Arrays and Slices. The question text is about an array and the code is illustrating using a slice. Therefore there two questions are implied; pass a single item slice, and pass a single item array.
An array: var a [1]string
A slice: var s []string
Passing a single item slice to the function:
func SliceFunc( slc []string) {
fmt.Println(slc)
}
func main() {
a := "stringy"
SliceFunc( []string{a} )
// or an actual array to the same function
b := [...]string { "thingy" }
SliceFunc( []string{b[0] )
}
Passing a single item array to the function.
Here there is an issue, as an array has a fixed length and as a parameter to a function it cannot accept different length arrays so we are left with working function which has limited flexibility:
func ArrayFunc( arr [1]string) {
fmt.Println(slc)
}
func main() {
var a [1]string
a[0] = "stringy"
ArrayFunc( a )
}
It seems that as a generalization sticking to slices is a more flexible solution.
(If you would like more on Slices and Arrays here one blog by Andrew Gerrand covering go slices usage and internals.)
You can utilize append or make:
package main
import "fmt"
func main() {
{
var a []string
a = append(a, "north")
fmt.Println(a)
}
{
a := make([]string, 1)
a[0] = "north"
fmt.Println(a)
}
}
https://golang.org/pkg/builtin
I have a map of values that looks like this:
vals := map[string]interface{}{"foo": 1, "bar": 2, "baz": 7}
data := map[string]interface{}{"bat": "obj", "values": vals}
What should my template look like to generate the following string (note the correct comma usage)?
SET obj.foo=1, obj.bar=2, obj.baz=7
I started with this as my template:
SET {{range $i, $v := .values}} {{.bat}}.{{$i}}={{$v}},{{end}}
But that just prints out
SET
And even if that did work, the commas would be incorrect. I then tried to use a custom function to format the map, but I couldn't get the template to ever call my function. None of the following seemed to work:
SET {{.MyFunction .values}}
SET {{call .MyFunction .values}}
SET {{call MyFunction .values}}
when MyFunction was defined as:
func MyFunction(data map[string]interface{}) string {
fmt.PrintLn('i was called!')
return "foo"
}
And I'm executing the templates using a helper function that looks like this:
func useTemplate(name string, data interface{}) string {
out := new(bytes.Buffer)
templates[name].Execute(out, data)
return string(out.Bytes())
}
Thanks!
This will get you pretty close:
SET {{range $key, $value := $.values}}{{$.bat}}.{{$key}}={{$value}} {{end}}
rendering as:
SET obj.bar=2 obj.baz=7 obj.foo=1
Unfortunately, I don't think there's any simple way to have the commas added in between the values due to how the range action iterates on maps (there's no numeric index). That said, the template packages were meant to be easily extensible so you can have less logic in your templates and more logic in Go itself, so it's easy enough to code a helper function in Go and make it available to your templates.
If you're happy to go that extra mile, then the template becomes much simpler, and also more efficient. The function can look like this:
func commaJoin(prefix string, m map[string]interface{}) string {
var buf bytes.Buffer
first := true
for k, v := range m {
if !first {
buf.WriteString(", ")
}
first = false
buf.WriteString(prefix)
buf.WriteByte('.')
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(fmt.Sprint(v))
}
return buf.String()
}
and your template would look like:
SET {{$.values | commaJoin $.bat}}
Here is a working example with this logic:
http://play.golang.org/p/5lFUpFCzZm