2

I am getting an integer, representing a duration in minutes, but if larger than a specific value, I would like to display it as hours, or as days, but I would still like to keep the ng-model value in minutes, so if the user makes any change to the value, and my app reads it back, it should be in minutes.

For example: If I read '480 minutes', the display should say 8 (hours). If I read '1440 minutes', it should be 1 (day). If the user changes that to 0.5 (day), then the value in ng-model should be 720.

I would like to keep the number part in an input, while the measurement unit (minutes/hours/days) in a label on the right of the input.

I created a 'duration' filter that looks like this:

myApp.filter('duration', function() {
  //Returns duration from minutes in hours
    return function(minutes) {
      var hours = Math.floor(minutes / 60);
      return hours;
    }
});

However, when I add it to the following element

...list iteration through fields, where textField is the current iteration object
    <input type="number" class="text-input" ng-model="textField.CustomFieldValue | duration">

it shows an error message in the console:

[ngModel:nonassign] Expression 'textField.CustomFieldValue | duration' is non-assignable. Element: <input type="number" class="text-input ng-pristine ng-untouched ng-valid" ng-model="textField.CustomFieldValue | duration">

I know my filter is not correct (yet), but I just used it for a little testing.

I am not really that familiar with AngularJS, so I might be using the filter in a wrong way.

The filter IS working, however, so on a value of 480, I do get 8 hours displayed, but I am concerned about the error message in the console.

2
  • How user has know if value in input is hours or days? I think it should be directive and not filter Commented Sep 20, 2017 at 10:06
  • The duration values are always stored & sent in minutes, but the app should be able to display them in different formats, WHILE still keeping the values in minutes in the logic, so when it sends it again, it is in minutes. The user would know if it's displayed in minute/hour/day, by the label that is next to the number input. Ok, so a directive should be used instead of a filter? Commented Sep 20, 2017 at 10:08

1 Answer 1

3

I don't think attaching the filter in the ng-model is a good idea! It will give you a max-digest error mostly. Here is my solution to this, Implement ng-model formatters and parsers, Here is awesome article to get you started on this!

Formatters and Parsers

Basically for my solution, I create new directives which will implement the logic for hours and days, the formatters does the logic and the parser reverses the logic, else the ng-model will loose the original value. Check my below snippet!

var app = angular.module('myApp', []);

app.controller('MyController', function MyController($scope) {
  $scope.textField = 480;
});

app.directive('timeParser', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope: {
      timeParser: "@"
    },
    link: function(scope, element, attr, ngModel) {
      ngModel.$formatters.push(function(minutes) {
        if (scope.timeParser === "hours") {
          return Math.floor(minutes / 60);
        } else if (scope.timeParser === "days") {
          return Math.floor(minutes / (60 * 24));
        } else {
          return minutes;
        }
      });

      ngModel.$parsers.push(function(minutes) {
        if (scope.timeParser === "hours") {
          return Math.floor(minutes * 60);
        } else if (scope.timeParser === "days") {
          return Math.floor(minutes * (60 * 24));
        } else {
          return minutes;
        }
      });

    }
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app="myApp">
  Minutes
  <input type="number" class="text-input" ng-model="textField">
  <br> Hours
  <input type="number" class="text-input" ng-model="textField" time-parser="hours">
  <br> Days
  <input type="number" class="text-input" ng-model="textField" time-parser="days">
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, amazing reply! Thank you very much Naren. Just one question: is it possible to combine it into one directive? I would love to not create multiple inputs and show/hide them dynamically, so only one is displayed.
@Laureant Sure why not! here is the method to do that! (updated my answer) I have put one way binding for the input type (time-parser="hours"), you can change this if you want!

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.