Suppose you have 3 input text fields - f1, f2, f3. And want you want is to have f3 a "computed" field based on what's the values on f1 and f2. Simple enough, right? Not exactly.
If you do did something like this:
<div ng-app> <div ng-controller="CTRL"> <input type="text" ng-model="f1" /> <input type="text" ng-model="f2" /> <input type="text" value="{{total()}}" /> <p>{{'Angular works!'}}</p> </div> </div>
And your Angular script is like this:
function CTRL ($scope) { $scope.f1= 3; $scope.f2= 4; $scope.total = function() {return $scope.f1 + $scope.f2;}; }
You are in for a bad time. You will notice it will work at the start but when you change the value on either f1 or f2, the total field is showing a concatenated string and not a sum. DAFUQ! Peculiarity #1. The fix is actually pretty easy if you use a directive.
var app = angular.module('intDirective', []); app.directive('integer', function(){ return { require: 'ngModel', link: function(scope, ele, attr, ctrl){ ctrl.$parsers.unshift(function(viewValue){ return parseInt(viewValue); }); } }; });
To use this is to add a ng-app="intDirective" property to the root div and the input tags should look like this:
<div ng-app='intDirective'> <div ng-controller="CTRL"> <input type="text" ng-model="f1" integer/> <input type="text" ng-model="f2" integer/> <input type="text" value="{{total()}}" /> <p>{{'Angular works!'}}</p> </div> </div>
OK, its looking good but try typing in a character on either f1 or f2? Yes, another thing we have do. We have to check the value being typed it is not shit (sometimes called Validation).
var app = angular.module('intDirective', []); var INTEGER_REGEXP = /^\-?\d*$/; app.directive('integer', function(){ return { require: 'ngModel', link: function(scope, ele, attr, ctrl){ ctrl.$parsers.unshift(function(viewValue){ ctrl.$parsers.unshift(function(viewValue) { if (INTEGER_REGEXP.test(viewValue)) { // it is valid ctrl.$setValidity('integer', true); return parseInt(viewValue); } else { // it is invalid, return undefined (no model update) ctrl.$setValidity('integer', false); return undefined; } }); } }; });
What's left is a simple $watch function to mimic Knockout computed fields (Peculiarity #2). Just add this snippet inside the controller.
$scope.$watch(function(){ return $scope.val1 + $scope.val2; }, function(newValue, oldValue){ $scope.computed = newValue; });
There.