Search code examples
javascriptangularjsrevealing-module-pattern

How should I package plugin functions for use in a validation directive?


I am working on the below validation directive, suggested to me in this answer:

MyBigAngularApp.directive("bkNgValidation", function ($compile) {
    return {
        priority: 10000,
        terminal: true,
        link: function (scope, element, attrs) {

            var validationType = attrs.bkNgValidation;
            window["addValidationFor_" + validationType](element);

            // prevent infinite loop
            element.removeAttr("bk-ng-validation");
            $compile(element)(scope);
        }
    };
});

Then, when I apply this directive to an html element, in the form, bk-ng-validation="phoneNumber", my directive invokes this function:

function addValidationFor_phoneNumber(element) {
    element.attr("ng-pattern", "/^[0-9]+$/");
    element.attr("ng-minlength", 5);
    element.attr("ng-maxlength", 8);
    alert("yeah baby");
}

This addValidationFor_phoneNumber is currently in the global namespace, just for my proof of concept, but I am looking to maybe use a revealing module to organize what could become quite a number of validation functions. Or is there some other pattern I should follow because I am working inside Angular? I suspect I could do something like declare a constant for the revealing module and inject it into the directive, but I thought I'd ask this question before going too far down the wrong road.


Solution

  • Indeed it is generally not recommended to use variables from the global scope in Javascript, and an absolute anti-pattern when working with AngularJS.

    What you are looking for is a service (or factory, which does the same job in a slightly different syntax), which would be injected to your directive.

    MyBigAngularApp.service('bkService', function() {
      this.phoneNumber   = function(element) { ... }
      this.somethingElse = function(element) { ... }
    });
    

    And your directive becomes:

    // Note how bkService is injected to the directive in this first line
    MyBigAngularApp.directive("bkNgValidation", function ($compile, bkService) {
      return {
        priority: 10000,
        terminal: true,
        link: function (scope, element, attrs) {
    
            var validationType = attrs.bkNgValidation;
            bkService[validationType](element);
    
            // prevent infinite loop
            element.removeAttr("bk-ng-validation");
            $compile(element)(scope);
        }
      };
    });
    

    Now if the only directive that will use this service is that one, you don't really need to create a service but can simply wrap all these functions as private methods from bkNgValidation:

    MyBigAngularApp.directive("bkNgValidation", function ($compile) {
    
      var validations = {
        phoneNumber:   function(element) { ... }
        somethingElse: function(element) { ... }
      };
    
      return {
        priority: 10000,
        terminal: true,
        link: function (scope, element, attrs) {
    
            var validationType = attrs.bkNgValidation;
            validations[validationType](element);
    
            // prevent infinite loop
            element.removeAttr("bk-ng-validation");
            $compile(element)(scope);
        }
      };
    });