Displaying error messages on submission with Angular Formly

blog >> development

Tue Dec 29 2015

Angular Formly in an Angular app I am working on as an alternative to creating Angular forms in HTML. I have found the HTML approach leads to validation and control logic for the form being split between both the HTML (with many ng-if, ng-show, ng-required/ required and ng-change directives sprinkled everywhere) and the JavaScript controller for the form.

This makes a consistent architecture for validation and control logic difficult to maintain. It also leads to a lot of duplicated HTML in controller templates and duplicated validation logic everywhere.

Angular Formly provides what I hope will be a reprieve from this mess!

After ripping out a smaller form in my app and reconstructing it in the object literal/functional approach that Angular Formly champions I was able to get a very nice layout with the Angular Formly: Material Templates inspired by Angular Material (which has recently hit v1.0 at the time of this writing).

I needed to customize the way error/validation messages were displayed to the user, with the following requirements.

  • Validation UI signaling occurs after an element loses focus and is invalid (red border around the field)
  • Validation messages are displayed only after submitting the form with invalid elements, even if they have never had interaction from the user
  • These rules should be applied globally instead of per-field or per-form

To meet these requirements I updated my app's config function

function config($locationProvider, formlyConfigProvider) {
  $locationProvider.html5Mode(true).hashPrefix('!');

  formlyConfigProvider.extras.errorExistsAndShouldBeVisibleExpression = function($viewValue, $modelValue, scope) {
    return (scope.fc.$invalid && scope.form.$submitted);
  };
}

By setting the formlyConfigProvider.extras.errorExistsAndShouldBeVisibleExpression to a function I can determine if the error message should be displayed for each field based on the field's validity (scope.fc.$invalid) and the form's submission status ( scope.form.$submitted). I also updated my app's run function.

function run(formlyValidationMessages) {
  formlyValidationMessages.addStringMessage(
    'required',
    'This field is required',
  );
}

Which provides a default error message on all fields that are defined with templateOptions: { required: true } or with validators: { required: function($viewValue, $modelValue, scope) { ... } }. I then created a form in my controller template.

<form novalidate ng-submit="vm.onSubmit()">
  <formly-form model="vm.model" fields="vm.fields" form="vm.form">
    <div class="col col-third">
      <div class="row">
        <button type="submit" class="btn btn-primary">Save Changes</button>
      </div>
    </div>
  </formly-form>
</form>

The problem was when I submitted the form the Validation UI signaling appeared but the error message was not displayed. When I set breakpoints in app.js for the errorExistsAndShouldBeVisibleExpression function I saw that scope.form.$submitted was always false even after I had just hit the button to submit the form.

The problem was on line one of the above controller.html. Although I had defined a form on my formly-form directive I had not specified the form name attribute on the form element. The form connected to the formly-form was not the form I was submitting when pressing the button.

To fix this I added a name attribute to my form element.

<form novalidate ng-submit="vm.onSubmit()" name="vm.form">
  <formly-form model="vm.model" fields="vm.fields" form="vm.form">
    <div class="col col-third">
      <div class="row">
        <button type="submit" class="btn btn-primary">Save Changes</button>
      </div>
    </div>
  </formly-form>
</form>

Now when I submit the form, even if I have not edited any fields, the validation messages will appear for all elements that are required or have any other validation errors.