Skip to content

Components and Directives

The Stratpoint Timesheet Application uses custom AngularJS directives defined directly on the main StratpointTSApp module. All directives are implemented in /public/app/js/mine/directives.js. This section details the actual custom directives and their implementation.

Actual Directive Implementation

All Directives are Defined On Main Module

// All directives are defined directly on StratpointTSApp in /public/app/js/mine/directives.js
StratpointTSApp
.directive('directiveName', function() {
    // Implementation
})

Loading Management

Loading Container Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('loadingContainer', function () {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            var loadingLayer = angular.element('<div class="loading"></div>');
            element.append(loadingLayer);
            element.addClass('loading-container');
            scope.$watch(attrs.loadingContainer, function(value) {
                loadingLayer.toggleClass('ng-hide', !value);
            });
        }
    };
})

Usage:

<div loading-container="isLoading">
    <!-- Content that will show loading overlay -->
</div>

Permission Management

Permission-Based Visibility Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('hasPermission', ["Authenticate", function(Authenticate) {
  return {
    link: function(scope, element, attrs) {
      if(!angular.isString(attrs.hasPermission))
        throw "hasPermission value must be a string";

      var value = attrs.hasPermission.trim();
      var notPermissionFlag = value[0] === '!';
      if(notPermissionFlag) {
        value = value.slice(1).trim();
      }

      function toggleVisibilityBasedOnPermission() {
        var hasPermission = Authenticate.hasPermission(value);
        if( !(hasPermission && !notPermissionFlag || !hasPermission && notPermissionFlag ) ){
            element.remove();
        }
      }

      toggleVisibilityBasedOnPermission();
      scope.$on('permissionsChanged', toggleVisibilityBasedOnPermission);
    }
  };
}])

Usage:

<!-- Hide element if user doesn't have permission -->
<button has-permission="72">Approve Timelogs</button>

<!-- Hide element if user HAS permission (negation) -->
<div has-permission="!57">You don't have basic access</div>

<!-- Permission IDs are numeric strings -->
<div has-permission="94">Project Management</div>

Form Validation

Field Matching Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('match', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
        scope: {
            match: '='
        },
        link: function(scope, elem, attrs, ctrl) {
            scope.$watch(function() {
                var modelValue = ctrl.$modelValue || ctrl.$$invalidModelValue;
                return (ctrl.$pristine && angular.isUndefined(modelValue)) || scope.match === modelValue;
            }, function(currentValue) {
                ctrl.$setValidity('match', currentValue);
            });
        }
    };
})

Usage:

<input type="password" ng-model="password" name="password">
<input type="password" ng-model="confirmPassword" match="password" name="confirmPassword">

Number Conversion Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('convertToNumber', function() {
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, ngModel) {
            ngModel.$parsers.push(function(val) {
                return val != null ? parseInt(val, 10) : null;
            });
            ngModel.$formatters.push(function(val) {
                return val != null ? '' + val : null;
            });
        }
    };
})

Usage:

<input type="text" ng-model="numericValue" convert-to-number>

Data Export

CSV Export Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('exportCsv', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var fn = $parse(attrs.exportCsv);
            element.on('click', function (event) {
                scope.$apply(function () {
                    fn(scope, {$event: event});
                });
            });
        }
    };
}])

Usage:

<button export-csv="exportTableData($event)">Export to CSV</button>

Template Management

Static Include Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('staticInclude', ["$http", "$templateCache", "$compile", function($http, $templateCache, $compile) {
    return {
        restrict: 'A',
        transclude: true,
        link: function(scope, element, attrs) {
            var templatePath = attrs.staticInclude;
            $http.get(templatePath, {cache: $templateCache}).success(function(response) {
                element.html(response);
                $compile(element.contents())(scope);
            });
        }
    };
}])

Usage:

<div static-include="app/partials/common/footer.html"></div>

UI Enhancement

Table Enhancement Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('table', ["$compile", "$timeout", function($compile, $timeout) {
    return {
        restrict: 'E',
        link: function(scope, element, attrs) {
            // Add responsive table wrapper
            if (!element.parent().hasClass('table-responsive')) {
                element.wrap('<div class="table-responsive"></div>');
            }

            // Add Bootstrap table classes
            element.addClass('table table-striped table-hover');

            // Add sorting indicators to headers
            $timeout(function() {
                element.find('th[data-sortable]').each(function() {
                    var header = angular.element(this);
                    if (!header.find('.sort-indicator').length) {
                        header.append('<span class="sort-indicator"><i class="fa fa-sort"></i></span>');
                    }
                });
            });
        }
    };
}])

Permission Panel Height Adjustment Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('adjustPermissionPanelHeight', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            $timeout(function() {
                var windowHeight = $(window).height();
                var panelOffset = element.offset().top;
                var availableHeight = windowHeight - panelOffset - 100; // 100px buffer

                element.css('max-height', availableHeight + 'px');
                element.css('overflow-y', 'auto');
            }, 100);
        }
    };
}])

Confirmation Redirect Directive

// From /public/app/js/mine/directives.js
StratpointTSApp
.directive('confirmRedirect', ["$q", "$uibModal", "$location", "$timeout", function($q, $uibModal, $location, $timeout) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.on('click', function(event) {
                event.preventDefault();

                var modalInstance = $uibModal.open({
                    template: '<div class="modal-header"><h3>Confirm Navigation</h3></div>' +
                             '<div class="modal-body"><p>Are you sure you want to leave this page?</p></div>' +
                             '<div class="modal-footer">' +
                             '<button class="btn btn-primary" ng-click="ok()">Yes</button>' +
                             '<button class="btn btn-default" ng-click="cancel()">Cancel</button>' +
                             '</div>',
                    controller: function($scope, $uibModalInstance) {
                        $scope.ok = function() {
                            $uibModalInstance.close();
                        };
                        $scope.cancel = function() {
                            $uibModalInstance.dismiss('cancel');
                        };
                    }
                });

                modalInstance.result.then(function() {
                    $location.path(attrs.confirmRedirect);
                });
            });
        }
    };
}])

Usage:

<a href="#" confirm-redirect="/dashboard">Go to Dashboard</a>

Summary

All directives in the Stratpoint Timesheet Application are:

  1. Defined directly on StratpointTSApp module (no separate directive modules)
  2. Located in single file: /public/app/js/mine/directives.js
  3. Focused on specific functionality: permissions, forms, UI enhancement, data export
  4. Tightly integrated with the application's authentication and UI patterns

The directives provide essential functionality for: - Permission-based UI control - Form validation and enhancement - Loading state management - Data export capabilities - Template inclusion - UI improvements and responsive behavior