Form Validation

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 File backend/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 "";
}
1
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
    }
1
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)
          }
        }
      }
    }
1
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.