Formie Icon Formie

Write your own parsing logic for the Calculations field

The Calculations field is a powerful field type, allowing you to take other fields' content and combine them in a number of different ways. But, you can also provide your own logic for how to handle values!

Josh Crawford Josh Crawford Oct 2022

The Calculations field is a read-only field that takes content from other fields' content. There's loads you can do, from arithmetic, comparing values and even logic. Keep reading on the docs.

You might like to include your own logic for how values in the Calculations field are treated. Under the hood, we use a JavaScript implementation of the Symfony Expression Syntax (opens new window) for handling parsing of the formula you provide for the Calculations field. You can even write your own, via JavaScript — which is what we're covering in this guide, along with how to use events to modify data.

Let's look at some practical examples.

Currency#

You might be referencing two Number fields in your Calculations field. Something like:

{fieldA} + {fieldB}

{# Given `fieldA` = 10 and `fieldB` = 5, outputs 15 #}

But what if we wanted to format this as a currency value, say as USD? We can do this with the fields' afterEvaluate JS event. This event is fired every time a referenced field's value changes — so in this case whenever fieldA or fieldB changes values.

So firstly, let's create 3 fields:

  • Number (handle fieldA)
  • Number (handle fieldB)
  • Calculations (handle result)

Ensure that the result field has the formula {fieldA} + {fieldB} by picking the other two Number fields with the variable picker.

Insert the following JavaScript on the page where your form is rendered.

// Fetch the Calculations field we want to format values for
const $field = document.querySelector('[data-field-handle="result"]');

// Listen to every time the formula is evaluated
$field.addEventListener('afterEvaluate', function(e) {
    const formatter = new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency: 'USD',
    });
    
    e.detail.result = formatter.format(e.detail.result);
});

Here, we bind the afterEvaluate event to the result field. This will fire after the evaluation has run, and we have the result. In this case, we just want to format it, so we take the result (e.detail.result) and run it through our formatter, then set that value.

Rendering the field, we'll now see $0.00 as soon as it loads (rather than 0), and the value will be shown as a currency!

Rounding#

In almost an identical method to the above, we might also like to round numbers.

// Fetch the Calculations field we want to format values for
const $field = document.querySelector('[data-field-handle="result"]');

// Listen to every time the formula is evaluated
$field.addEventListener('afterEvaluate', function(e) {
    e.detail.result = Math.round(e.detail.result);
});

Given the values 1.276 and 7.682 for fieldA and fieldB, respectively — the result will be 9.

Modifying data#

So far, we've covered modifying the result generated by the evaluation, but we can also modify the content used in the formula, and the formula itself using the beforeEvaluate JS event.

Let's say the fieldA in our example has some validation logic you want to enforce. If the value entered is over 20, then you want that to be the maximum value. You could of course enforce this on the fieldA Number field itself, but this is just an example after all 😉

Insert the following JavaScript on the page where your form is rendered.

// Fetch the Calculations field we want to format values for
const $field = document.querySelector('[data-field-handle="result"]');

// Listen to every time the formula is evaluated
$field.addEventListener('beforeEvaluate', function(e) {
    const formula = e.detail.formula;
    const variables = e.detail.variables;
    
    if (e.detail.variables.field_fieldA > 20) {
        e.detail.variables.field_fieldA = 20;
    }
});

You can see we're modifying the variables object to enforce our logic. Every value in the variables object is prefixed by field_, then the field handle. These are the source fields content, used in our formula.

Now, when setting values, fieldA values can still be entered over 20 (which might be valid for your use case), but the calculations will max out at 20.

Advanced usage#

Where the variables in the previous example were simple numbers, you can also supply these as functions — because JavaScript! We also might like to modify the formula, to get real tricky. 😎

// Fetch the Calculations field we want to format values for
const $field = document.querySelector('[data-field-handle="result"]');

// Listen to every time the formula is evaluated
$field.addEventListener('beforeEvaluate', function(e) {
    // Modify the formula (or you could supply this in the field settings)
    e.detail.formula = 'field_fieldA + field_fieldB + suffix.myMethod("🔥")';

    // Add a new variable `suffix` with a method
    e.detail.variables.suffix = {
        myMethod: function(string) {
            return ' is ' + string;
        },
    };
});

Given the values 1 and 5 for fieldA and fieldB, respectively — the result will be 6 is 🔥.

Get started with Formie

Available for Craft 4. Get it from the plugin store.