<!DOCTYPE html>
<html ng-app="angularjs-starter">
  
  <head lang="en">
    <meta charset="utf-8">
    <title>Custom Plunker</title>
    <script src="https://raw.github.com/jquery/globalize/master/lib/globalize.js"></script>
    <script src="//code.angularjs.org/1.1.1/angular.js"></script>
    <link rel="stylesheet" href="style.css">
    <script>
      document.write('<base href="' + document.location + '" />');
    </script>
    <script src="app.js"></script>
  </head>
  
  <body ng-controller="MainCtrl">
    <form name="dayForm">
      <ng-form name="startSubForm">
        <time-picker label="Start of the day" model="startMinutes"></time-picker>
      </ng-form>
      <ng-form name="endSubForm">
        <time-picker label="End of the day" model="endMinutes" start></time-picker>
      </ng-form>    
      
      <span>TODO: setup ng-show: Start time must be before end time!</span>
    </form>
    <pre>dayForm = {{ dayForm }}</pre>
    <pre>dayForm.$valid = {{ dayForm.$valid }}</pre>
    <pre>dayForm.$error = {{ dayForm.$error }}</pre>
    
    <pre>startMinutes={{startMinutes}}</pre>
    <pre>endMinutes={{endMinutes}}</pre>
    
    <hr/>
    <h2>Problems:</h2>
    <ul>
      <li>How to display error messages in the timePicker.html template? Should I pass formName as scope property and do something like this[formName].timePicker.$error.timeFormatter?</li>      
      <li>How to use startEndValidator to compare start/end times?</li>
    </ul>
  </body>

</html>
/* CSS goes here */
var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {
  // Save hour in minutes on the model to avoid storing strings.
  $scope.startMinutes = 10 * 60;
  $scope.endMinutes = 18 * 60;
});

app.directive('timePicker', function() {
  return {
      scope: {
          label: '@',
          model: '='
      },
      restrict: 'E',
      replace: true,
      templateUrl: 'timePicker.html',
      transclude: false
    };
});

app.directive('timeValidator', function($timeConverter, $log) {
   return {
    restrict: 'A',
    require: 'ngModel',
    link: function (scope, element, attrs, ngModel) {
      //model -> view 
      ngModel.$formatters.unshift(function(modelValue) {           
          $log.log(1, modelValue);
          try {
              var newViewValue = $timeConverter.timeToString(modelValue);
              ngModel.$setValidity('timeValidator', true);
              return newViewValue;
          } catch(err) {
              $log.log(1, err);
              ngModel.$setValidity('timeValidator', false);
          }
          return modelValue;
      });

      //view -> model
      ngModel.$parsers.unshift(function (viewValue) {
          $log.log(2, viewValue);
          try {
              var newModelValue = $timeConverter.timeFromString(viewValue);
              ngModel.$setValidity('timeValidator', true);
              return newModelValue;
          }
          catch (err) {
              ngModel.$setValidity('timeValidator', false);
              // what should I return here?
              // I don't want to change model values here as input is invalid
              return ngModel.$modelValue;
          }
      });

      // Listen for change events to enable binding
      element.bind('blur keyup change', function () {
          scope.$apply(read);
      });

      // Write data to the model
      function read() {
          var val = element.val();
          ngModel.$setViewValue(val);
      }
    }
  };
});

app.directive('startEndValidator', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attr, ctrl) {
      var startWidget = elm.inheritedData('$formController')[attr.startEndValidator];

      ctrl.$parsers.push(function(value) {
        var startValue = startWidget.$modelValue;
        var endValue = ctrl.$modelValue;
        
        if (startValue < endValue ) {
          ctrl.$setValidity('startEndValidator', true);
          return value;
        }
        ctrl.$setValidity('startEndValidator', false);
        // should I not return anything here?
      });
/*
      how to do the validation other way round?
      startWidget.$parsers.push(function(value) {
        ctrl.$setValidity('startEndValidator', value === ctrl.$viewValue);
        return value;
      });*/
    }
  };
});

app.service('$timeConverter', function() {
  // format time from minutes to string
  function timeToString(model) {
    if (model == null) {
        return null;
    }

    if (typeof model !== 'number') {
        throw 'model must be a number!';
    }

    if (model <= 0) {
        throw 'model must be greater than 0';
    }

    var minutes = model % 60;
    model -= minutes;

    var hours = model / 60;

    var days = Math.floor(hours / 24);

    hours = hours - days * 24;

    var resultParts = [];
    if (days > 0) {
        resultParts.push(days);
        resultParts.push(' ');
    }

    resultParts.push(Globalize.format(hours, 'd2'));
    resultParts.push(':');
    resultParts.push(Globalize.format(minutes, 'd2'));

    return resultParts.join('');
  }

  // parse time string to number of minutes
  function timeFromString(str) {
    if (!str) {
        return null;
    }

    var m = str.match(/^(\d{2})\:(\d{2})$/);
    if (m == null) {
        throw 'not a valid time format ' + str;
    }

    var hour = parseInt(m[1]),
        minute = parseInt(m[2]);

    if (hour < 0) {
        throw 'hour (' + hour + ') cannot be < 0!';
    }

    if (hour > 23) {
        throw 'hour (' + hour + ') cannot be > 23!';
    }

    if (minute < 0) {
        throw 'minute (' + minute + ') cannot be < 0!';
    }

    if (minute > 59) {
        throw 'minute (' + minute + ') cannot be > 59!';
    }

    return hour * 60 + minute;
  }

  return {
      timeToString: timeToString,
      timeFromString: timeFromString
  };
});
<div>
  <span>{{label}}</span>
  <!-- plan here is to change type to hidden later on and use some better UI for timepicker -->
  <input type="text" name="timePicker" ng-model="model" required time-validator start-end-validator=""/>  
  <span ng-show="timePicker.$error.required">***</span>
  <span ng-show="timePicker.$error.timeFormatter">Incorrect time format!</span>
  <pre>timePicker.$valid={{timePicker.$valid}}</pre>
  <pre>timePicker.$error.required={{timePicker.$error.required}}</pre>
  <pre>timePicker.$error.timeFormatter={{timePicker.$error.timeFormatter}}</pre>
</div>