4

In Angular.js I'm passing a number to my view which is the length of time something takes in minutes.

<p>{{time.minutes}}</p>

I want to format this value so it displays in hours and minutes e.g (6h 42m). I tried using some of the inbuilt date functions but couldn't get anywhere. Is it better to do this sort of logic in the controller before passing it to the view or is it possible in the view?

1
  • 1
    I think you need a simple filter imo if you have got that value in a ng-repeat loop its better to attach a InnerController to the mark up like tr data-ng-controller="InnerController" data-ng-repeat="user in users like gist.github.com/whisher/423136e0b5aa5d372924 Commented Aug 24, 2014 at 10:01

3 Answers 3

6

As what Whisher mentioned, a filter is more appropriate in changing the format of your html output.

I have created a filter not long ago, that can convert the given time value based on the unit of measure in time(hours, minutes, seconds) into a specific format that suits the taste of its user.

DEMO

JAVASCRIPT

  .filter('time', function() {

    var conversions = {
      'ss': angular.identity,
      'mm': function(value) { return value * 60; },
      'hh': function(value) { return value * 3600; }
    };

    var padding = function(value, length) {
      var zeroes = length - ('' + (value)).length,
          pad = '';
      while(zeroes-- > 0) pad += '0';
      return pad + value;
    };

    return function(value, unit, format, isPadded) {
      var totalSeconds = conversions[unit || 'ss'](value),
          hh = Math.floor(totalSeconds / 3600),
          mm = Math.floor((totalSeconds % 3600) / 60),
          ss = totalSeconds % 60;

      format = format || 'hh:mm:ss';
      isPadded = angular.isDefined(isPadded)? isPadded: true;
      hh = isPadded? padding(hh, 2): hh;
      mm = isPadded? padding(mm, 2): mm;
      ss = isPadded? padding(ss, 2): ss;

      return format.replace(/hh/, hh).replace(/mm/, mm).replace(/ss/, ss);
    };
  });

HTML USAGE

<!-- 
  65 minutes converted to hh:mm:ss format which is the default format = 01:05:00 
  The parameter 'mm' suggests the the time value(65) is a unit of measure in minutes.
-->
<pre>{{65 | time:'mm'}}</pre>

<!-- 
  65 minutes converted to the OP's desired output = 1h 5m 

  the parameter 'hhh mmm' suggests the format of the output desired
  by the OP, the "hh" and "mm" text are replace with the hour value and
  the minute value

  the last parameter which is a boolean value suggests that the hour(hh), minute(mm),     
  second(ss) values are not padded. E.G. hour = 2 output would be 02. By default, this
  parameter is set to true.
-->
<pre>{{65 | time:'mm':'hhh mmm':false}}</pre>
Sign up to request clarification or add additional context in comments.

3 Comments

Very good solution, thank you. I added a few extra lines to it, though, to replace the (s) with either an actual 's' or a blank string depending on the actual numbers. See demo here: plnkr.co/edit/RGsOzkQ0rlqxsshI7c6f?p=preview
I realize the age of the OP, but how would you suppress the values if they are 0? For example, "1h 0s" should just display "1h".
I have the same question, how to remove the 0h. thanks for posting this!
2

What about this:

One line solution

<pre>
 with hours: {{ (time.minutes<60) ? 
    (time.minutes) + 'm' : (time.minutes%60==0) ?
     (time.minutes-time.minutes%60)/60 + 'h' :
    ((time.minutes-time.minutes%60)/60 + 'h' + ' ' + time.minutes%60 + 'm') }}
</pre>

Demo 1 Fiddle


Suggested solution

JS

 if($scope.time.minutes < 60){
        $scope.time.result = ($scope.time.minutes) + 'm';        
    }
    else if($scope.time.minutes%60==0){
        $scope.time.result = ($scope.time.minutes-$scope.time.minutes%60)/60 + 'h';        
    }
    else{
         $scope.time.result = (($scope.time.minutes-$scope.time.minutes%60)/60 + 'h' + ' ' + $scope.time.minutes%60 + 'm');
    }

HTML

<pre>with hours: {{ time.result}}</pre>

Demo 2 Fiddle

Test:

minutes = 29 ->  Output '29m'
minutes = 60 ->  Output '1h'
minutes = 123 -> Output '2h 3m'

More suggested way (Directive)

Directive

app.directive('myHours', function () {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            myData: '='
        },
        template: '<pre>with hours: {{myData.result}}</pre>',
        link: function (scope, elem, attrs) {

            var calc = function (time) {
                if (time < 60) {
                    return (time) + 'm';
                } else if (time % 60 == 0) {
                    return (time - time % 60) / 60 + 'h';
                } else {
                    return ((time - time % 60) / 60 + 'h' + ' ' + time % 60 + 'm');
                }
            }

            scope.myData.result = calc(scope.myData.minutes);
        }
    };
});

HTML

<my-hours my-data="time"></my-hours>

Demo 3 Fiddle

1 Comment

The above directive sets or overwrites the field "result" on whatever object you pass it, which can have sneaky side-effects depending on how it is used. Use "scope.result" and {{result}} instead of "scope.myData.result" and {{myData.result}} - see fiddle: jsfiddle.net/andyhasit/sqcjun8z/2
1
(function () {
    'use strict';

    angular
        .module('module_name')
        .filter('minutesToDateTime', minutesToDateTime);

    function minutesToDateTime() {
        return function(minutes) {
            return new Date(1970, 0, 1).setMinutes(minutes);
        };
    }
})();

{{minutes | minutesToDateTime | date:'HH:mm:ss'}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.