QuickForm can create calculators of any complexity, including those with dynamic fields and dynamic field groups. To do this, the component has four types of calculators that calculate by a formula, without a formula, and also from the logic written in a php file. Calculators are designed for the average level of the user, but you will need knowledge of mathematics. In this article, I will try to describe each of the four types and give simple examples of how they work. To return to the QuickForm page, follow the link: QuickForm.

## 1. Calculator type default.

Let's make some simple form, for example, calculation of plastic windows. Let it look like this:

Actually, I don't know how plastic windows are calculated, but it doesn't matter now. This is not necessary to explain how the calculator works. I assume that the cost will be proportional to the area, so I entered the width, height and type of double-glazed window (number of layers). Also introduced two additional options that are added to the final cost.

The form consists of four groups of fields: main and three dependent. The main group contains radio buttons for selecting the number of leaves. After selecting a value, the corresponding dependent group is loaded. All math is in dependent groups and can be completely different for different cases. The set of fields may also differ.

The main difference of this type is the absence of a formula. This allows you to do the calculation without being tied to the id of the fields, which means it allows you to make dynamic calculators. Mathematical logic is written directly in the field settings (math parameter), and if this field appears in the form, this math is simply added to the calculation.

For example, if we want to output the width of the window in meters as a result, then the math of the width field will be: v/1000. If you want to display the area of the window, then the math of the height field will be: *v/1000. The result is v/1000*v/1000. Next, we will add math to the double-glazed window field, multiplying the area by the cost (let the cost of 1 meter of square glass be 320 rubles). For a single-chamber double-glazed window: *2*320, for a double-glazed window: *3*320. As a result, we got our calculator.

In addition, we add math to the checkboxes: +80 for the slopes and +200 for the window sill. Just go to the field settings and write in math: +80.

Thus, QuickForm provides a calculator that is accessible for understanding and development, which is configured by a person with any level of training. You just need to be able to add prime numbers, multiply and place brackets.

The disadvantages of the method.

1. The default calculator type only outputs one result.

2. It is difficult to implement logic that requires the field to be reused in a further calculation.

## 2. Calculator type multiple.

This type of calculator uses a set formula to calculate. It can output multiple results and also reuse field values.

Formula structure: 3.4={2.2}*{1.1}+{2.3}*{1.1}, where 3.4 is the id of the calculatorSum field. The curly brackets enclose the id of the calculated fields. Mathematical logic can be written both in the formula and in the math settings of the fields themselves. For example, if the math field {2.2} contains the following entry: (45+34)*7, then the formula would be calculated like this: 3.4=(45+34)*7*{1.1}+{2.3}*{1.1}.

Let's create a real calculator and see how it works with an example. Let it be a shipping cost calculator.

The form of this calculator consists of the main and three dependent groups. The main group contains radio buttons for choosing a vehicle, a google maps script, a hidden field that accepts a distance along the route, and three calculatorSum fields. Dependent groups contain the "Weight" field and an auxiliary hidden field needed to convey the speed of the vehicle (to calculate the delivery time).

The calculator formula is as follows:

199.5 = {199.3};

hours = {199.5} /({200.1}{201.1}{202.1});

199.6 = {hours}<1 ? 1 : {hours};

weight = {200.0}{201.0}{202.0} + 0;

199.7 = {199.5}*0.7 + {199.5}*{weight}/10;

You can open the developer tools in your browser and take a closer look at how this calculator works. Below I will analyze his formula line by line so that there are no questions left.

1. First line of the formula: 199.5 = {199.3}; is the delivery distance. The entry means that QuickForm should take math from the field with id 199.3 and display the result in the calculatorSum field with id 199.5. Field 199.3 is an auxiliary hidden field into which the google maps script passes the distance. The map script is added only for clarity, and this field can be an ordinary text field in which the user himself will write some numbers. I just wanted to, google maps is not necessarily needed here. The math of this field contains "v", which means the current value of the field. Thus, in "Delivery distance" the value of this field is simply written.

2. The second line of the formula calculates the delivery time and writes it to the hours variable. In essence, we simply divide the delivery distance by the speed of the vehicle. The speed is transmitted by an auxiliary hidden field through the dependent group. Since we always have only one vehicle selected, the expression ({200.1}{201.1}{202.1}) will contain only one value. For example, if we have selected a bike messenger, we have a field {200.1} whose math is 15 (let's say its speed is 15 km/h). There are no fields {201.1}{202.1} in the form, so nothing is substituted for them: hours = {199.3} / ({200.1});

You can stop there and display the value in calculatorSum. That is, write: 199.6 = {199.3} /({200.1}{201.1}{202.1}); But I need the delivery time to never be less than one hour. Therefore, in calculatorSum I write through the condition: 199.6 = {hours}<1 ? 1 : {hours};

3. We calculate the cost of delivery by the expression: Cost = distance * 0.7 + distance * weight / 10. For convenience, I have deduced a separate variable weight = {200.0}{201.0}{202.0} + 0; In principle, the field where the user specifies the weight of the delivered goods could be specified not in the dependent, but in the main group. Then it would be one and it would be much easier. But I wanted the maximum shipping weight to match the type of vehicle. Therefore, there are three fields with weight. Only one of them is always active. The zero at the end is added in order to avoid an error at the stage of loading the page. After all, the dependent field is loaded into the form with some delay, which means there will be a moment in time when none of the three fields will be in the form. Then the variable weight would take an empty (non-zero) value, and the mathematical expression would be incorrect: 199.7 = {199.5}*0.7 + {199.5}*/10;

The disadvantages of the method.

1. Binding formula to field id. This is not always convenient, for example, in a dynamic calculator. If the same field dynamically appears multiple times in a form, it will have the same id but may have different values. Of course, such calculators are very rare, however, this must be taken into account.

Finally, here is the google maps script that I used in this form. Perhaps someone will come in handy.

```
var script = document.createElement('script');
script.src = 'https://maps.googleapis.com/maps/api/js?key=api_key&callback=initMap';
script.async = true;
document.head.appendChild(script);
window.initMap = function() {
// create a map with the city of Amsterdam
var map = new google.maps.Map(document.getElementById("map"), {
zoom: 5,
center: {
lat: 52.3745403,
lng: 4.89797551
},
scrollwheel: false
});
// enable route service
var directionsService = new google.maps.DirectionsService();
var directionsRenderer = new google.maps.DirectionsRenderer({
map,
suppressMarkers: true,
});
// when changing the route, recalculate the distance
directionsRenderer.addListener("directions_changed", () => {
const directions = directionsRenderer.getDirections();
if (directions) {
computeTotalDistance(directions);
}
});
// Set the starting points of the route
var origin = new google.maps.LatLng(52.308226730765526, 4.76868436142579);
var destination = new google.maps.LatLng(52.372439457020796, 4.865501378027353);
// Create a fixed marker (warehouse)
var marker1 = new google.maps.Marker({
position: origin,
draggable: false,
map: map,
icon: {
url: '/images/calc/airport.png',
scaledSize: new google.maps.Size(80, 80),
}
});
// Create a draggable marker (car)
var marker2 = new google.maps.Marker({
position: destination,
draggable: true,
map: map,
icon: {
url: '/images/calc/deliv.png',
scaledSize: new google.maps.Size(50, 50),
}
});
// We will redraw the route after each move of the dragged marker
marker2.addListener('dragend', handleEvent);
function handleEvent(event) {
destination = new google.maps.LatLng(event.latLng.lat(), event.latLng.lng());
displayRoute();
}
// Draw the starting route (after the page has loaded)
displayRoute();
// Route draw function
function displayRoute() {
directionsService.route({
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING,
})
.then((result) => {
directionsRenderer.setDirections(result);
})
.catch((e) => {
alert("Could not display directions due to: " + e);
});
}
// Distance recalculation function
function computeTotalDistance(result) {
let total = 0;
const myroute = result.routes[0];
if (!myroute) {
return;
}
for (let i = 0; i < myroute.legs.length; i++) {
total += myroute.legs[i].distance.value;
}
// We write the distance in the form field with id = "dist" (our auxiliary hidden field).
var dist = document.getElementById("dist");
dist.value = total / 1000;
// Ask quickform to recalculate the form
QuickForm.sumForm(dist.form);
}
};
```

The script can be displayed inline using the customHtml field or programmatically, using customPhp. Instead of api_key you need to substitute your value.

## 3. Calculator type simple.

Let's create another simple calculator. Let it be a loan calculator. To calculate, we use the formula from Wikipedia: AP = CA*((IR+100)/1200)/(1-(1+(IR+100)/1200)^(-NM)) for an annuity payment. Here we have NM - number of months, IR - interest rate, CA - loan amount.

This type of calculator differs in that in the math field setting, not a mathematical expression is entered, but the name of the variable. For example, NM is written in math for the number of months. The formula for this calculator is as follows:

204.3 = {CA}*(({IR}+100)/1200)/(1-(1+({IR}+100)/1200)**(-{NM}));

The disadvantages of the method.

1. The calculator considers only fields with value filled in by the user: number, text, range types.

## 4. Calculator type custom.

This is the most functional and convenient type of calculator. My choice in most cases, except for the simplest ones. To work with "custom" you need to know php.

In this version of the calculator, a code is written in the formula field. For example, the code for the current calculator is:

```
$customQFcalculator = function($project, $data)
{
require_once(QF3_PLUGIN_DIR . 'classes/example.php');
$exampleCalculator = new QuickForm\exampleCalculator($project, $data);
$calculatorSum['206.1'] = $exampleCalculator->getSin();
$calculatorSum['206.2'] = $exampleCalculator->getCos();
return $calculatorSum;
};
```

This code is a customQFcalculator($project, $data) function that takes two variables: $project - all parameters of the current project and $data - all parameters of the fields submitted by the form. Returns an array whose keys are the fieldids of the required calculatorSum and whose values are the sum to insert into them.

To make it convenient, I created the example.php file in a convenient place for me and wrote all the calculation logic there. The content of this file in this case is as follows:

```
namespace QuickForm;
\defined('QF3_VERSION') or die;
class exampleCalculator extends qfCalculator
{
protected $angle = 0;
public function __construct($project, $data)
{
$this->data = $data;
$this->angle = $this->getAngle();
}
protected function getAngle()
{
foreach($this->data as $field) {
if(isset($field->math) && $field->math == 'angle') {
return (float) $field->value;
}
}
}
public function getSin()
{
return round(sin(deg2rad($this->angle)),8);
}
public function getCos()
{
return round(cos(deg2rad($this->angle)),8);
}
}
```

To pass the field value to the example.php file, I used the math field parameter. This is convenient, because as a string, you can set as many different values for each field as you like. In my case, the math field says "angle".

Good luck.