Friday, March 30, 2018

Added a generateForm() Method to vadacl

Today I published a new version of my vadacl library that includes a new convenience method:  generateForm().  The generateForm() method accepts a data object and returns a FormGroup with FormControl instances generated from the properties and any vadacl-based validation configurations associated with the data object.

So instead of declaring the FormGroup and FormControl instances manually:

export class CruiseShipComponent extends Vadacl implements OnInit {
  cruiseShip: CruiseShip;
  shipForm: FormGroup;

  ngOnInit() {
    this.cruiseShip = new CruiseShip();
    this.shipForm = new FormGroup({
      'name': new FormControl( this.cruiseShip.name, this.applyRules( this.cruiseShip, 'name' ) ),
      'cruiseLine': new FormControl( this.cruiseShip.cruiseLine, this.applyRules( this.cruiseShip, 'cruiseLine' ) ),
      'yearBuilt': new FormControl( this.cruiseShip.yearBuilt, this.applyRules( this.cruiseShip, 'yearBuilt' ) )
    });
  }

...you can simply invoke generateForm() with the data object:

...
ngOnInit() {
  this.cruiseShip = new CruiseShip();
  this.shipForm = this.generateForm( this.cruiseShip );
}

generateForm() also accepts a second arguments:  a "mods" object that allows you to customize the FormGroup returned by the method.  The possible modification properties are:
  • exclude:  an array of data object properties you don't want to create a FormControl for.
  • only:  an array of the only data object properties you want to create FormControls for (overrides the "exclude" property if you mistakenly use both).
  • rename: an object literal for remapping a data object property name (key) to a different FormControl name (value).
  • validations: an object literal of additional validations to apply to particular data object properties.

An example of using the mods argument:
this.shipForm = this.generateForm( this.cruiseShip, {
  exclude: [ 'yearBuilt' ],
  rename: { cruiseLine: 'company' },
  validations: {
     name: { maxLength: { maxLength: 100, message: 'Ship name cannot be longer than 100 characters' } }
  }
});

The Angular application I maintain on GitHub to demonstrate vadacl has been updated with a demo that uses generateForm(). 

Thursday, December 28, 2017

Angular Update Guide:

Earlier today when I wanted to retest my vadacl library against the latest version of Angular (5.1.2), I skimmed through the Angular blog post regarding the 5.0 release, and at the bottom of the post was a mention of a new upgrade tool:  the Angular Update Guide.



The guide is a Firebase-backed Angular web application styled with Angular Material.  You enter the Angular version you're currently running, the version you want to upgrade to, answer some general questions and press the button.  What you get is a set of brief (at least in my case) instructions for how to perform the upgrade, and what kind of changes you may need to make to your code before and after the update.

I'm glad the Angular team made this tool.  Updating the semver version of the Angular packages in the package.json file is usually simple:  it's knowing what the versions of other dependencies like rxjs or TypeScript need to be in order to make it work that's trickier.  And this takes care of that.

Inadvertent Example of the Benefit of vadacl's Class-Based Validation Rules

While I was verifying that my vadacl validation library wasn't adversely affected ("broken") by any changes in the latest version of Angular (5.1.2), I discovered that one of my validations wasn't working correctly.

In two of the forms in the vadacl demo I have up on GitHub, there is a "Gender" form field where the user is only allowed to enter "M" or "F".  As I was testing the validation on that field, I was surprised to see that while the "F" validation was being enforced, the "M" validation was allowing any word that started with the letter "M".

The validation rule was defined within the UserProfile object class in the demo:  one of the main features of vadacl is that you can define your validations in a class, like so...
validations: { [ index: string ] : PropertyValidations } = {
    firstName: {
      maxLength: { maxLength: 25 },
      required: {}
    },
    lastName: {
      maxLength: { maxLength: 25, message: 'Your last name cannot be longer than 25 characters.'},
      required: { message: 'Your last name is required.' }
    },
    username: {
      maxLength: { maxLength: 30, message: 'Your username cannot be longer than 30 characters.'},
      required: { message: 'You must have a username.' }
    },
    password: {
      minLength: { minLength: 6, message: 'Your password must be at least 6 characters long.' },
      required: { message: 'You must provide a password.' },
    },
    age: {
      pattern: { pattern: '[0-9]*', message: 'Enter your age as an integer.' }
    },
    gender: {
      pattern: { pattern: 'M|F', message: 'Enter your gender as "M" or "F".' }
    }
};

...and then utilize them in your components. The rule invoked the Pattern Validator method using the following pattern:

M|F

I thought that the either/or pipe would limit the valid value to either "M" or "F", but apparently not, and by virtue of being in front of the pipe the male validation allowed for characters beyond the "M".  So I fixed the validation by changing the pattern to:

[MF]{1}

If this same issue had come up in a different application that used normal Angular reactive form validation, I would have had to correct the pattern in the form controls in both of the components, rather than just once.

 

Thursday, September 14, 2017

New Version of vadacl Released as an NPM Package

It's been a LONG time since my last blog post.  Work and life simply kept me from doing a lot of personal coding.  But I'm making an effort to get back into the swing of things, and I decided to start that effort by moving forward with my vadacl Angular validation library.

For anyone not familiar with what vadacl is about, here's the current synopsis:

vadacl is a library that extends and enhances reactive form validation in Angular 4.x. It provides:

  • A mechanism for declaring validation logic within domain classes / data objects that can be reused in multiple components.
  • The ability to configure the text of validation failure messages as part of the domain class validation logic or within a global validation message object.
  • Helper methods for triggering validation and displaying validation results.
  • Additional validation methods beyond those provided by Angular 4.x, and the ability to extend the vadacl validation methods with custom methods in your project.
  • The new version of vadacl (currently version 1.0.10) is available as an NPM package at:

    https://www.npmjs.com/package/vadacl

    There is also a separate GitHub repo containing an Angular CLI-powered Angular 4.x app that demonstrates the use of vadacl in different scenarios:

    https://github.com/bcswartz/vadacl-demo

    As part of the transition to an NPM package, vadacl was refactored to allow you to extend or override the validation methods and messages without touching the library files themselves, using files specific to your project.  That will let you update to future versions of vadacl via npm without losing any of your custom code.  The new version was written for Angular 4.x and includes vadacl versions of the newest methods in Angular's Validators class:  min, max, and email.

    The new demonstration application is an updated version of the one still hosted on the old vadacl GitHub repo, and contains not only examples of the new validation methods, but also provides documentation about how to go about extending vadacl and an example of how you can swap out the global messages file with a different one (designed for a different audience or different language) during your build/deployment process.

    Friday, December 30, 2016

    Version 0.2.0 of vadacl Released

    I just released a new version of my vadacl validation library for Angular 2. The new release includes the following updates:

    • To match a recent change to the pattern Validator in Angular 2, vadacl's pattern validation method was updated to accept both string and RegExp pattern arguments.

    • A requiredTrue validation method was added to parallel the recently-added Angular requiredTrue Validator (used primarily for validating that a checkbox has been checked/set to true).

    • The applyCollectionRule() method was added to the Vadacl class. The new method is designed to be used instead of the applyRules() method when applying a single validation method to a FormGroup or FormArray.

    • Added three new validation methods specifically for FormGroup and FormArray validation:

      • totals: validates that the sum of the numeric values of the FormGroup or FormArray controls equals a certain amount.

      • equalValues: validates that all of the values of the FormControls within a FormGroup or FormArray are exactly equal. Useful for performing password confirmation.

      • withinTrueCount: validates that the number of FormControls within a FormGroup or FormArray with a value of Boolean true falls within a given range. Designed primarily to validate how many checkboxes are checked.

    • Added and updated demos to demonstrate the new validation methods.

    • Updated the demo codebase to Angular 2.4.1

    Tuesday, November 29, 2016

    Locale-Based Message Support Added to vadacl

    The latest release of my vadacl validation library for Angular 2 introduces a new feature:  locale-based error message configuration.

    Prior to this release, vadacl would allow you to configure the error message for a particular validation error condition for a particular object property in either the object-based validation settings or in the component responsible for the form controls.  And if you did not declare an error message in either of those places, the validation method would return a method-specific default error message from the Messages object.  So in the following (slightly-contrived) scenario:

    
    // app/vadacl/locale/messages-en.ts
    let Messages = {
      /* DEFAULT LOCALE VALIDATOR ERROR MESSAGES */
      required: 'A value is required',
      ...
    }
    
    
    // app/domain/user-profile.ts
    ...
    export class UserProfile implements Validatable {
      firstName: string = null;
      lastName: string = null;
      username: string = null;
      age: number = null;
      gender: string = null;
    
      validations: { [ index: string ] : PropertyValidations } = {
        firstName: {
           required: { message: 'Your first name is required.' }
        },
        lastName: {
           required: {}
        },
        username: {
           required: { message: 'Username is required.' }
        },
        age: {
           required: {}
        },
        gender: {
           required: {}
        }
      };
      ...
    }
    
    // app/forms/user-profile-form.component.ts
    ...
    export class UserProfileForm extends Vadacl implements OnInit {
      profileForm: FormGroup;
      userProfile: UserProfile;
      ...
    
      ngOnInit() {
        this.userProfile = new UserProfile();
    
        this.profileForm = new FormGroup({
          'firstName': new FormControl(
             this.userProfile.firstName,
             this.applyRules( this.userProfile, 'firstName' )
          ),
    
          'lastName': new FormControl(
             this.userProfile.lastName,
             this.applyRules( this.userProfile, 'lastName', { required: { message: 'Your last name is required.' } )
          ),
    
          'username': new FormControl(
             this.userProfile.username,
             this.applyRules( this.userProfile, 'username', { required: { message: 'Please enter a username.' } )
          ),
    
          'age': new FormControl(
             this.userProfile.age,
             this.applyRules( this.userProfile, 'age' )
          ),
    
          'gender': new FormControl(
             this.userProfile.gender,
             this.applyRules( this.userProfile, 'gender' )
          )
        });
      }
      ...
    }
    
    ...the validation error message for each UserProfile property in the UserProfileForm template that fails the required validation would be:
    • firstName: "Your first name is required."
    • lastName: "Your last name is required." (the message is provided during the FormControl instantiation)
    • username: "Please enter a username." (the component-level message overrides the object-level message)
    • age: "A value is required"
    • gender: "A value is required"

    Now vadacl provides the option to configure error messages for object properties in the Messages object rather than in the validation settings in the object. Refactoring the example above to use the new feature, the code would look like:

    
    // app/vadacl/locale/messages-en.ts
    let Messages = {
      /* DEFAULT LOCALE VALIDATOR ERROR MESSAGES */
      required: 'A value is required',
      ...
    
      /* LOCALE-BASED DOMAIN CLASS MESSAGES */
      UserProfile: {
        firstName: {
          required: 'First name is required.'
        },
        lastName: {
          required: 'Last name is required.'
        },
        username: {
          required: 'Username is required.'
        }
      }
      ...
    }
    
    // app/domain/user-profile.ts
    ...
    export class UserProfile implements Validatable { 
      ...
      validations: { [ index: string ] : PropertyValidations } = {
        firstName: { required: {} },
        lastName: { required: {} },
        username: { required: {} },
        age: { required: {} },
        gender: { required: {} }
      };
      ...
    }
    
    // app/forms/user-profile-form.component.ts
    ...
      'lastName': new FormControl(
        this.userProfile.lastName,
        this.applyRules( this.userProfile, 'lastName' )
      ),
    
      'username': new FormControl(
        this.userProfile.username,
        this.applyRules( this.userProfile, 'username', { required: { message: 'Please enter a username.' } )
      ),
    ...
    
    ...and the error messages would now be:
    • firstName: "First name is required."
    • lastName: "Last name is required."
    • username: "Please enter a username."
    • age: "A value is required"
    • gender: "A value is required"

    The validation message defaults to the message in the Messages object that matches the object / property / validation method combination. If a different message is defined for that property in the object validation settings (which ordinarily you wouldn't do if you wanted to use the locale-based messages in the Messages object) or in the component when configuring the FormControl, that other message would become the message value returned when validation fails.

    Configuring the validation messages in the Messages object allows you to keep all of the object-level messages in one place.  In theory, this should also give developers who need to perform internationalization the option of creating Messages objects for different languages and then altering which Messages file is imported into validation-methods.ts file as a build step prior to compiling the app.

    Friday, November 11, 2016

    vadacl: A Library For Streamlining Form Validation in Angular 2

    The initial version of my TypeScript-based vadacl library for performing form validation in Angular 2 is now available on GitHub.

    vadacl provides the following enhancements to the typical implementation of form validation via the reactive form classes (FormControl, FormGroup, FormArray, and FormBuilder):

    • Instead of configuring all of the validation in the component hosting the form, you can configure certain validations within the data object itself (validation rules that should remain consistent wherever the data object is used in your application), then add to or modify those validation rules in the component to create the final set of validations needed for a given form.

    • The vadacl validation methods add a "message" property to the metadata object returned when the form data fails validation.  This "message" property value is the message meant to be presented to the user, and can be configured and/or overridden at multiple levels:

      • The method level, via a set of default message values
      • The data object
      • The component level
    • The Vadacl class, whether used as a superclass for your component or as a service, provides methods for providing an array of validator methods to the FormControls in your form and for displaying the validation error "message" values in your template, removing the need to add multiple DOM elements with "ngIf" directives to display each kind of validation error or to add code to your component to gather and translate validation failures into error messages.

    vadacl is (currently) a small library contained in a single folder you can just drop into your Angular application. The GitHub repo contains that folder as part of a small Angular 2.1.1 application containing several working demos of vadacl in action.

    Enjoy!