Kimchi Man
Kimchi Man

Reputation: 1171

Updating only the 'view' value using angular directive

So I have a decimal value in controller like this:

// Controller
var MyController = function($scope) {
    ...
    $scope.percentValue = 0.05; // can be stored
    ...
};

<!-- View -->
<span>{{percentValue}}</span>
<input ng-model="percentValue" />

With the above code, the value in the input element is 0.05 - however, I want to allow a user to enter an integer value like 5.

So if the $scope.percentValue is 0.05, I want to show it as 5 in the input element. And if a user enters 5, the $scope.percentValue should be 0.05.

However, the tricky thing here is I only want to update the view value - meaning that the span element should still show 0.05. Only the value in the input element should be 5.

I am trying to achieve this with ngModel, but I am still struggling.

This is what I have now:

var MyDirective = function() {
    function link(scope, element, attrs, ngModel) {
        ngModel.$render = function() {
            element.val(ngModel.$viewValue || '');
        };

        ngModel.$formatters.push(function (value) {
            return value * 100;
        });

        element.on('change blur', function() {
            ngModel.$setViewValue(element.val());
        }); 
    }

    return {
        restrict: 'A',
        require: '?ngModel',
        scope: {},
        link: link
    };
};

Please advise!!

Upvotes: 0

Views: 1018

Answers (4)

miqh
miqh

Reputation: 3664

Including my comment as an answer because it seemed to help. :-)

To summarise: since you've already provided a $formatters function for your directive, which converts a model value ($modelValue) to displayed form ($viewValue), it's simply a matter of providing a $parsers function to do the reverse and convert any user input back to the model value.

Example Plunker

Upvotes: 1

Icycool
Icycool

Reputation: 7179

Other than creating a filter you can also calculate on the template

<span>{{percentValue * 100}}</span>

Upvotes: 0

aorfevre
aorfevre

Reputation: 5064

I'd create a filter for percentage :

angular.module('myModule')
.filter('percentage', ['$filter', function($filter) {
    return function(input, decimals) {
        return $filter('number')(input*100, decimals)+'%';
    };
}]);

The input will store integer (such as 5)

<input ng-model="percentValue" />

But I'll add a filter to the span part :

<span>{{percentValue | percentage:2}}</span>

Credit to https://stackoverflow.com/a/21727765/3687474 for the filter directive.

Upvotes: 0

bkbooth
bkbooth

Reputation: 498

What you're trying to achieve is probably possible, but I would find it really confusing to read the code. The simplest solution that I think would solve your problem and maintain readability is to store an integer value (5) in $scope.percentValue, so that ng-model is always dealing with an integer when typing and displaying the value in the <input>. Then create a custom filter and use it to output the value as 0.05 in the <span>.

Edit: adding a concrete code example. Play with it here: https://plnkr.co/edit/C1cX2L9B2GM2yax1rw7Z?p=preview

JS:

var MyController = function ($scope) {
  $scope.percentValue = 5;
};

function formatPercent (input) {
  return input / 100;
}

var myApp = angular.module('MyApp', []);
myApp.filter('percent', function () { return formatPercent });
myApp.controller('MyController', ['$scope', MyController]);

HTML:

<body ng-controller="MyController">
  <span>{{ percentValue | percent }}</span>
  <input ng-model="percentValue">
</body>

Upvotes: 0

Related Questions