Supercharge your Web Forms using Angular - Formly

+ No comment yet


In this edition, Abhijeet Thakur (abhijeet.thakur@indexnine.com) talks about Angular-formly.

Forms, as we all know, are a vital part of web development. All of us who are familiar with AngularJS will agree with the fact that how easy it is to develop robust scalable from-ends using AngularJS.

But it comes with its own problems, one of the biggest being how to deal with big forms. The HTML bloats up, becomes unreadable to human-eye, too many tags, validations in each of the numerous fields and many more! We faced this in one of our projects. This project has more than 20 forms with each of them containing at least 60 fields. We had to invest a lot of time and effort into finding a workable solution to this recurring problem.

We found an optimum solution in the form of 'Angular-Formly’- an open source module maintained by Kent C.Dodds. As the official documentation also says, Angular-Formly gives your forms consistency, maintainability, simplicity, flexibility and sanity!

Angular-Formly suggests maintaining less HTML in favour of JavaScript.

An example of this ideology is cited on the angular-formyl site as well. Using the module we can go from:
<form>
   <div class="form-group">
      <label for="exampleInputEmail1">Email address</label>
      <input type="email" class="form-control" id="exampleInputEmail1"                                 placeholder="Enter email" ng-model="vm.user.email">
   </div>
   <div class="form-group">
      <label for="exampleInputPassword1">Password</label>
      <input type="password" class="form-control" id="exampleInputPassword1"                           placeholder="Password" ng-model="vm.user.password">
   </div>
   <div class="form-group">
      <label for="exampleInputFile">File input</label>
      <input type="file" id="exampleInputFile" ng-model="vm.user.file">
      <p class="help-block">Example block-level help text here.</p>
   </div>
   <div class="checkbox">
      <label>
      <input type="checkbox" ng-model="vm.user.checked"> Check me out
      </label>
   </div>
   <button type="submit" class="btn btn-default" ng-click="vm.submit(vm.user)">                  Submit                                                                         </button>
</form>

To only this:
<formly-form model="vm.user" fields="vm.userFields">
    <button type="submit" class="btn btn-default" ng-click="vm.submit(vm.user)">                    Submit                                                                      </button>
</formly-form> 
And use the power of Javascript to do the rest:

function YourCtrl() {
      var vm = this;

      vm.user = {};

      // note, these field types will need to be
      // pre-defined. See the pre-built and custom templates
      // http://docs.angular-formly.com/v6.4.0/docs/custom-templates
      vm.userFields = [
        {
          key: 'email',
          type: 'input',
          templateOptions: {
        type: 'email',
        label: 'Email address',
        placeholder: 'Enter email'
          }
        },
        {
          key: 'password',
          type: 'input',
          templateOptions: {
        type: 'password',
        label: 'Password',
        placeholder: 'Password'
          }
        },
        {
          key: 'file',
          type: 'file',
          templateOptions: {
        label: 'File input',
        description: 'Example block-level help text here',
        url: 'https://example.com/upload'
          }
        },
        {
          key: 'checked',
          type: 'checkbox',
          templateOptions: {
        label: 'Check me out'
          }
        }
      ];
    }

Getting started with Angular-Formly

For elaboration purpose we will create a custom form with 3 fields - first name,last name and email using Angular-Formly.

To get started you will need to install angular-formly and some dependencies.You can also use bower if your are familiar with it.
1) Add the following files in index.html
<!-- angular and required files for angular formly -->
<script src="lib/angular-formly/api-check.min.js"></script>
<!-- For Validations in Angular formly using ngMessage required angular version is 1.4.7 -->
<script src="lib/angular/angular_v1_4_7.min.js"></script>
<script src="lib/angular-formly/ui-bootstrap-tpls.min.js"></script>
<script src="lib/fileInput/fileinput.min.js"></script>
<script src="lib/angular-formly/formly.min.js"></script> 
<script src="lib/angular-formly/angular-formly-templates-bootstrap.min.js"></script>       
<script src="lib/angular-message/angular-messages.min.js"></script>
<script src="lib/angular-formly/angular-animate.min.js"></script> 

2.  Now add formly and formlyBootstrap as a dependency in app.js. You can also add ngAnimate and ngMessages which shows error messages at the time of validation.
// app.js
(function() {

  'use strict';
  angular.module('formlyApp', ['formly', 'formlyBootstrap','ngAnimate','ngMessages']);

})();
3. Now set up angular formly form in HTML.
<body ng-app="formlyApp" ng-controller="MainController as vm">
   <div class="container col-md-4 col-md-offset-4">
      <form novalidate>
         <h1>Customer Details</h1>
         <formly-form model="vm.customer" fields="vm.customerFields"                                     form="vm.customerForm">
            <button type="submit" class="btn btn-primary" ng-disabled="vm.customerForm.$invalid"> Submit</button>
         </formly-form>
      </form>
   </div>
</body>
4. Set Up the Controller

Create an array called vm.customerFields with the objects describing the fields in the form.
Key: Form field objects need a unique key which generally describes what the purpose of the field.

Type: The form types such as input, textarea, select, radio etc needs to be registered with the formlyConfig service/provider. (Already done for the Bootstrap templates)

TemplateOptions: This is an object where we further describe the properties for the field like - the kind of input, label and placeholder, whether the field is required. To add more fields in the form just add more objects into this array. This is very easy to maintain and edit anytime.

This is how the code snippet looks:
// scripts/MainController.js
...

function MainController(province) {

    var vm = this;

    // The model object that we reference
    // on the  element in index.html
    vm.customer = {};
    
    // An array of our form fields with configuration
    // and options set. We make reference to this in
    // the 'fields' attribute on the  element
    vm.customerFields = [
        {
            key: 'first_name',
            type: 'input',
            templateOptions: {
                type: 'text',
                label: 'First Name',
                placeholder: 'Enter your first name',
                required: true
            }
        },
        {
            key: 'last_name',
            type: 'input',
            templateOptions: {
                type: 'text',
                label: 'Last Name',
                placeholder: 'Enter your last name',
                required: true
            }
        },
        {
            key: 'email',
            type: 'input',
            templateOptions: {
                type: 'email',
                label: 'Email address',
                placeholder: 'Enter email',
                required: true
            }
        },
    ];
    
}

...
The form is now ready with 3 fields. You can perform custom validations for each field, disable fields, create custom fields using templates etc. (One of the best example of custom fields is here).

You can also set a wrapper to use this field globally.

An example where we used the ngMessage directive to show error messages for invalid inputs in a form. Using this wrapping made the whole process of displaying error messages very simple.
app.config(function (formlyConfigProvider) {
   formlyConfigProvider.setWrapper({
      name: 'validation',
      types: ['input'],
      template:'<formly-transclude></formly-transclude><div ng-messages="fc.$error" ng-if="form.$submitted || options.formControl.$touched" class="error-messages"><div ng-message="{{ ::name }}" ng-repeat="(name, message) in ::options.validation.messages" class="message">{{ message(fc.$viewValue, fc.$modelValue, this)}}</div></div>'
   });
})

The Gist

Hopefully we were able to elaborate some of the advantages of using Angular-Formly. It has very good documentation with tons of examples. There are prebuilt templates for Bootstrap,LumX, and Vanilla HTML and WIP template libraries for angular material,Iconic and foundation. It is very easy to create custom templates and reuse them. Configuring validations is a cake walk.

if you are a newcomer to angular, you should check out the official angular-formly website. You can get in touch with the module’s maintainer, Kent.C.Dodds for any questions or help. He is super responsive to any queries.

References:

Post a Comment