Search code examples
gogo-templatesgo-html-template

How to calculate a total for a column inside a golang template?


I have this code inside a html/template:

{{ $TotalPrice := 0.0 }}
{{ range $i, $tx := .Transactions }}
{{ $TotalPrice := FloatInc $TotalPrice (StrToFloat .TotalPrice) }}
  <tr>
    <td>{{ inc $i 1 }}</td> 
    <td>{{ .Description.String }}</td>
    <td>{{ .Type }}</td>
    <td>{{ .TotalPrice }}</td>
    <td>{{ .Note }}</td>  
  </tr>  
{{ end }}
<tr>
  <td></td> 
  <td></td>
  <td></td>
  <td>{{ $TotalPrice }}</td>
  <td></td>
  <td></td>
</tr> 

Transactions are Money Transaction with TotalPrice DB Fields and I have 4 functions according Iris framework spec.

tmpl.AddFunc("dec", func(num int, step int) int {
    return num - step
})

tmpl.AddFunc("inc", func(num int, step int) int {
    return num + step
})

tmpl.AddFunc("FloatDec", func(num float64, step float64) float64 {
    return num - step
})

tmpl.AddFunc("FloatInc", func(num float64, step float64) float64 {
    return num + step
})

tmpl.AddFunc("StrToFloat", func(s string) (float64, error) {
    return strconv.ParseFloat(s, 64)
}) 

I've note the $TotalPrice keeps the initial value (0.0) for every iteration so {{ $TotalPrice }} inside the range will print the .TotalPrice value and the value of $TotalPrice at the last row will be 0.0 too then what is the equivalent, inside go template, of:

nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
    sum += num
}
fmt.Println("sum:", sum)

Solution

  • In Go's template, once you declare a variable and assign value to it, you can't change its value. What happen in your code are:

    1. The value of outer total price which is declared as $TotalPrice := 0.0 is always 0.0, and the variable scope is extended to the end of the template.
    2. When you defined a variable named $TotalPrice inside range, although the variable name is the same, a completely new variable will be allocated. The value assigned to this variable is given by FloatInc($TotalPrice, .TotalPrice). Note that argument $TotalPrice refers to outer total price which is 0.0, so the statement will be equal to $TotalPrice := 0.0 + .TotalPrice. Thus, when you print the $TotalPrice in each iteration, you got current .TotalPrice instead of accumulated total price.
    3. The scope of variable declared in (2) is between range and end. Thus when you print $TotalPrice at the last row, you got the value of outer total price declared in (1), i.e. 0.0.

    In your case, you need to declare a function which takes Transactions as its argument then calculate the total inside the function, e.g.

    tmpl.AddFunc("sum", func(transactions []Transaction) float64 {
        sum := 0.0
        for _, t := range transactions {
            if v, err := strconv.ParseFloat(t.TotalPrice, 64); err == nil {
                sum += v
            }
        }
        return sum
    })
    

    then use it in the template as:

    {{ $TotalPrice := sum .Transactions }}