Laureant
Laureant

Reputation: 1019

AngularJS display input value in a different format while keeping the same format in model

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.

Upvotes: 2

Views: 2684

Answers (1)

Naren Murali
Naren Murali

Reputation: 56600

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>

Upvotes: 3

Related Questions