Keats / tera

A template engine for Rust based on Jinja2/Django
http://keats.github.io/tera/
MIT License
3.36k stars 280 forks source link

Round a number with precision not work as expected! #923

Open mrhdias opened 1 month ago

mrhdias commented 1 month ago

I created a template to show a list of products with prices, but I wanted all prices to have two decimal places. To do this I used the Built-in filter round, but it doesn't work as expected if the variable is f32 and the decimal places are 00 instead of $XX.00 it puts $XX or if it is $XX.10 put $XX.1!

Does anyone have an idea of ​​how I can always put two decimal places in prices?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Store</title>
    <link rel="stylesheet" href="assets/css/store.css?v=2024060217">
</head>
<body>
    <header>
        <h1>Products List</h1>
    </header>
    <section class="container">
        <div class="sidebar">Sidebar</div>
        <ul class="product-list">
        {% for product in products %}
            <li>
                <a href="{{ product.permalink }}">
                    <img src="{{ product.images[0].src }}" alt="{{ product.images[0].alt }}">
                    {% set promotion = (1 - (product.price / product.regular_price)) * 100.0 %}

                    <div class="labels">
                        {% if product.stock_status == "outofstock" %}
                        <label class="outofstock">Out off stock</label>
                        {% endif %}
                        {% if product.on_sale and promotion > 0 %}
                        <label class="onsale">-{{ promotion | round }}%</label>
                        {% endif %}
                    </div>
                </a>
                <h2>{{ product.name }}</h2>
                <p>SKU: {{ product.sku }}</p>
                <p>Description: {{ product.short_description }}</p>
                <p>
                    {% if product.on_sale %}
                        <span class="sales_price">${{ product.regular_price | round(method="floor", precision=2) }}</span>
                    {% endif %}
                    <span class="regular_price">${{ product.price | round(method="floor", precision=2) }}</span>
                </p>
                <button {% if product.stock_status == "outofstock" %}disabled{% endif %}>Add To Cart</button>
            </li>
        {% endfor %}
        </ul>
    </section>

    {% include 'partials/footer.html' %}
</body>
</html>

store

Keats commented 1 month ago

So it's rounding correctly but we probably want a special formatting filter for floats.

mrhdias commented 4 weeks ago

The solution really involves making a filter.

use tera::{Result, Value};
use std::collections::HashMap;

// Register the custom filter
// tera.register_filter("round_and_format", round_and_format_filter);
fn round_and_format_filter(value: &Value, params: &HashMap<String, Value>) -> Result<Value> {
    let num = value.as_f64().ok_or_else(|| tera::Error::msg("Filter can only be applied to numbers"))?;
    let decimal_places = params.get("places")
        .and_then(Value::as_u64)
        .ok_or_else(|| tera::Error::msg("Filter parameter 'places' is required and must be a positive integer"))?;

    Ok(Value::String(format!("{:.1$}", num, decimal_places as usize)))
}

Example of usage:

<span class="regular_price">${{ product.price | round_and_format(places=2) }}</span>