Reputation: 791
I want to convert lenghts from mm to ft and inches or vice versa. User can input either mm or ft&in.The case is I always want to save data in mm to database but use angular to see it in both format.
I have already created a solution for it. http://plnkr.co/edit/thgx8vjsjxwfx6cLVVn1?p=preview But it is using ng-change everytime to convert the values.
I was wondering if some angular expert has better idea of doing similar stuff. Please note that I am only planning to save single value $scope.lengthmm
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
// $scope.lengthtodb = 0;
$scope.mmToFtIn =function(){
toInch = $scope.lengthmm * 0.0393701;
toFt = toInch/12;
OutputFT = Math.floor(toFt);
OutputInch = (toFt - Math.floor(toFt))*12;
$scope.lengthft = OutputFT;
$scope.lengthin = OutputInch;
$scope.Result1 = OutputFT + "ft" + OutputInch + "Inches";
};
$scope.FtInTomm =function(){
tomm = (($scope.lengthft *12)+ $scope.lengthin)*25.4;
$scope.lengthmm = tomm;
$scope.Result2 = tomm;
};
});
In addition, as there will be lots of fields using the the same logic maybe method mmToFTIn
needs to split in two methods to bind ft and inches separately. But I am really looking forward to see a smarted solution.
Upvotes: 0
Views: 2848
Reputation: 15876
Here is an improvement I made to Christoph Hegemann's answer.
The directive only uses one attribute, which points to a scope object inchesShowFeet
(you can call it anything you want).
<input type="text" ng-model="data.beamLength"
ng-enhanced-input="inchesShowFeet"
ng-model-options="{ debounce: 500 }" />
The directive itself, as he mentioned, uses the ng-model attribute's parsers
app.directive('ngEnhancedInput', function($parse){
return {
restrict: 'A',
require: 'ngModel',
link : function(scope, element, attr, ngModelCtrl){
ngModelCtrl.$parsers.unshift(scope.$eval(attr.ngEnhancedInput).store);
ngModelCtrl.$formatters.unshift(scope.$eval(attr.ngEnhancedInput).show);
}
};
});
The object, usually set in the controller, has a show and a store function. The name is funny, but it's the best I could think of. The one returns the value to show in the text box, the other one returns the value to store in the model.
$scope.inchesShowFeet = {
show: function(val){ return val / 12; },
store: function(val){ return val * 12; }
};
So, say I have 25
inches stored in my model. The show function get's called with a val
of 25. It divides it by 12 and returns it, which get's displayed in the text box. When you type in 4
, the store function get's called with a val
of 4. It multiplies it by 12 and returns 48, which get's stored in the model.
Upvotes: 0
Reputation: 424
One option is to create a $watch
on the millimeter variable and then update the inches model. This is probably a better option than ng-change because the $watch
will fire any time the variable is changed within the scope, not just when the input to your text box changes.
I have created an example for you. While you state you only wish to use one variable, I believe using a second variable in this manner is really the better approach. Alternatively you could use an object or an array to maintain a single variable while storing two values.
For this example I defined the variables as follows
$scope.inches = 0;
$scope.mm = 0;
Then we just create a watch on both variables. To reiterate, this function gets called any time there is a change to the mm
variable which allows you to ensure that the relationship between mm
and inches
is maintained.
$scope.$watch('mm', function(newVal, oldVal) {
$scope.inches = newVal / 25.4;
});
You could also create a custom filter as follows... this might even be a better solution if you only need to display mm
in a formatted manner (as opposed to using it for calculations or something else).
angular.module('myFilters', []).filter('mmToInches', function() {
return (function (input) {
return (input / 25.4);
});
});
Then later on do...
Inches Filter : {{ mm | mmToInches }}
You can add arguments to filters to a single filter can do both conversions.
angular.module('myFilters', []).filter('convert', function() {
return (function (input, type) {
if (type == 'mm2inch')
{
return (input / 25.4);
} else if (type == 'inch2mm') {
return (input * 25.4);
}
});
});
Inches Filter : {{ mm | convert:'mm2inch' }}
MM Filter : {{ inches | convert:'inch2mm' }}
Upvotes: 0
Reputation: 1434
Formatting the Output onto the view is best done with filters.
JS:
app.filter('inchFilter', function() {
return function(input) {
return Math.floor(input * 0.0393701);
};
});
HTML:
<input name="mm" type="text" value="{{lengthmm | inchFilter}}">
Edit:
For a more complete and powerful solution I extended the plunker with a directive to now allow two-way-binding on the non-metric fields aswell.
app.directive('enhancedInput', function($parse){
return {
restrict: 'A',
require: 'ngModel',
link : function(scope, element, attr, ngModelCtrl){
ngModelCtrl.$parsers.unshift(scope.$eval(attr.fromMm));
ngModelCtrl.$formatters.unshift(scope.$eval(attr.toMm));
}
};
});
This is achieved by first "requiring" the ngModelController and then using its $parsers and $formatters to intercept communication in between model and view.
Upvotes: 2