Guides
GuidesExamplesGitterLog In

Formly Expressions

Used throughout the library

There are four places where you can put expressions. The context in which these expressions are evaluated is important.

There are two different types of context and each is explained below:

expressionProperties, validators, & messages

These expressions can be functions or expression strings. If it's a function, it's invoked with the arguments $viewValue, $modelValue, and scope. The scope in this case, is the field's scope. If it's an expression string, it is evaluated using $scope.$eval with a locals object that has $viewValue and $modelValue (however, in the case of expressionProperties, $viewValue will simply be the $modelValue because ok into the ngModelController but we want to keep the api consistent).

Here's an example of formlyExpressions using both strings and functions:

vm.fields = [
  {
    type: 'input',
    key: 'bar',
    templateOptions: {required: true, label: 'IP Address'},
    expressionProperties: {
      'templateOptions.foo': '$modelValue', // set to the $modelValue of the control
      'templateOptions.required': 'model.foo === "foobar"'
    },
    hideExpression: function($viewValue, $modelValue, scope) {
      return scope.model.baz === 'foobar';
    }
    validators: {
      ipAddress: {
        expression: function($viewValue, $modelValue, scope) {
          var value = $modelValue || $viewValue;
          return /(\d{1,3}\.){3}\d{1,3}/.test(value);
        },
        message: '$viewValue + " is not a valid IP Address"'
      },
      notLocalHost: '$viewValue !== "127.0.0.1"'
    },
    validation: {
      messages: {
        required: 'to.label + " is required"'
      }
    }
  }
];

hideExpression

🚧

A few notes

These are evaluated in a similar way (but not the same way) as expressionProperties. Make sure to read the docs below to see how it differs. Also, this doesn't accept promises, you can only do synchronous operations here.

This is almost the same as expressionProperties with a slight difference. It's run in the context of the form rather than the field. The reason for this is if the field is hidden to start, then it's not yet been compiled and therefore doesn't actually have a scope yet. Formly tries to combat this by simulating the field's context. So most of the time you should experience the same behavior.

In the function form, you still get passed the $viewValue, $modelValue, and scope. However, the scope in this case is the formly-form scope (not the formly-field) and the $modelValue and $viewValue are actually both just the $modelValue.

In string form, you have access to options, index, formState, and formId in addition to everything that's available on the formly-form's scope.

watcher

🚧

Use expressionProperties

It is recommended that you use expressionProperties (see below) if you can rather than watcher. This is because each watcher adds a watcher to angular's $digest cycle and therefore can slow down your application, but expressionProperties of fields share a single watcher (on the model and formState) and therefore will perform better. Also, if you're just trying to be notified when something changes, use templateOptions.onChange instead :-)

expression and listener can be functions or expression strings. This is a regular angular $watch (depending on the specified type) function and it is created on the formly-form scope, despite being applied to a specific field. This allows the expressions to run even if the field's scope has been destroyed (via an ng-if like when the field is hidden). The function signature differs from a normal $watch however:

// normal watcher
$scope.$watch(
  function expression(theScope) {},
  function listener(newValue, oldValue, theScope) {}
);

// field watcher
$scope.$watch(
  function expression(field, theScope, stop) {},
  function listener(field, newValue, oldValue, theScope, stop) {}
);

Sponsor