Form Validation
Related Pages
Developer page
Article about display-formatters).
Calculated Fields
The target audience for this article is mDIS developers.
This article explains how the "Calculate" feature in the forms-manager works. It happens both on the server- and on the client-side. "Calculate" allows to create "calculated" or "derived fields" in an mDIS data entry form using short Javascript expressions. These "derived fields" are not stored in the database. That would be redundant, and also be a potential source of errors and inconsistencies.
Reminder
See "For Operators/Viewers" what Calculated Fields are.
Advanced
Server Side/Backend: See Yii behaviors. Client Side/Frontend: For complex multiline calculations/transformations see Dis-Form tutorial. For an alternative mechanism to calculate "derived fields", see also the Vue Slots part of that article.
Implementation
This is not a tutorial. Do not change any files mentioned in this article.
The transformation of the formula entered in "Calculate" to JavaScript is performed by
- the PHP method
getJsCalculate()
in Filebackend/components/templates/FormTemplateField.php
- Javascript in Vue Files:
src/components/DisSmartForm.vue
src/components/DisForm.vue
Backend
File backend/components/templates/FormTemplateField.php
:
/**
* Converts the content of the "calculate" field
* into a string appropriate for Javascript
* @return string
*/
public function getJsCalculate () {
if ($this->formInput['calculate'] > "") {
$calc = $this->formInput['calculate'];
$calc = trim($calc, "\t\n\r\0\x0B=");
// replace fields name with form model properties
$calc = preg_replace('/\[(\S*)\]/m', "this.formModel['$1']", $calc);
// replace absolute function
$calc = preg_replace('/ABS/m', "Math.abs", $calc);
return $calc;
}
return "";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Note the trim()
call. Besides removing preceding whitespace and trailing whitespace, it also removes any preceding =
characters from the user input.
Frontend
For each form input field marked as "calculated", file src/components/DisSmartForm.vue
takes the string returned from PHP method getJsCalculate()
and evaluates it as Javascript code, using eval()
(!):
calculatedFields () {
let calculated = {}
this.formTemplate.fields.filter(item => item.formInput.jsCalculate !== '')
.map(item => {
calculated[item.name] = function () {
return eval(item.formInput.jsCalculate)
}
})
return calculated
}
2
3
4
5
6
7
8
9
10
File src/components/DisForm.vue
assigns it to the Vue field watcher:
this.$watch('formModel', {
deep: true,
handler: () => {
for (const calculatedField in this.calculatedFields) {
try {
let cb = this.calculatedFields[calculatedField].bind(this)
let v = cb()
if (v) {
console.log('set ' + v)
this.formModel[calculatedField] = v
}
} catch (e) {
console.log('calc err', e)
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TBC
- For complex expressions that are needed in many forms, you should define a Yii behavior and/or a JS module, put it at the correct place in the filesystem, let Vue import it, and then use it like this... TBC-perhaps as independent article/tutorial.