slider & spinner bound with Angular

by PaulOD

HTML

<link rel="stylesheet" href="http://paulodonovan.net/DonateWebApp/vendor/powerange/powerange.css">
<script src="http://paulodonovan.net/DonateWebApp/vendor/powerange/powerange.js"></script>
<script src="http://code.angularjs.org/1.2.9/angular.js"></script>
<div ng-controller="DonationCtrl">
		<div id="donate">

			<h1>Donation:<spinner></spinner></h1>
			<slider></slider>
		</div>
		<div id="projects">
			<h2>Projects</h2>
			<ul>
				<li ng-repeat="project in projects">
					<h3>{{project.name}}</h3>
					<p>Raised so far: {{project.raised | currency: "€"}}</p>
					<p>Target: {{project.target | currency: "€"}}</p>
					<p>Your donation: {{preview.donation * project.ration  | currency: "€"}}</p>
					<progress-bar project="project" ></progress-bar>
				</li>
			</ul>
		</div>
	</div>

CSS

body {
			font-family: Arial, Sans-Serif;
        }

        p {
            margin: 0; line-height: 1em;
        }

        .bar {
			background: #EEE;
		}

		.barFill {
			background: green;
			height: 5px;
		}

		.range-quantity {background-color: green;}

        input#spinner {font-size: 1em; width: 8em; font-weight: bold; margin-left: .2em;}

JavaScript

angular.module('app', [])

.controller('DonationCtrl', function ( $scope ) {

	$scope.projects = [
		{
			name: 'Rainwater Harvesting Tanks',
			target: 10000,
			raised: 1000,
			ration: 0.4
		},
		{
			name: 'Girls Secondary School',
			target: 17000,
			raised: 5000,
			ration: 0.3
		},
		{
			name: 'Agriculture and Environmental Conservation',
			target: 50000,
			raised: 2000,
			ration: 0.2
		},
		{
			name: 'Underprivileged Children Program',
			target: 23000,
			raised: 5000,
			ration: 0.075
		},
		{
			name: 'Food & Seed Store',
			target: 27000,
			raised: 5500,
			ration: 0.025
		}
	];

	// Starting donation at 0
	$scope.preview = {donation: 0};
})


.directive('spinner', function () {
	return {
		restrict: 'E',
		template: '<span> €<input id="spinner" type="number" ng-model="preview.donation" value="{{preview.donation}}" max="10000" min="0" step="5"></span>',
		replace: true,
		link: function ( scope, elem, attr ) {
            scope.$watch('preview.donation', function (newVal, oldVal) {
                var spinnerNewVal = newVal;
                var spinnerOldVal = oldVal;
                console.log("spinnerNewVal : " + spinnerNewVal);
				console.log("spinnerOldVal : " + spinnerOldVal);
			});
		}
	};
})

.directive('slider', function () {
	return {
		restrict: 'E',
		template: '<input type="text" class="js-range" ng-model="preview.donation"/>',
		replace: true,
		link: function ( scope, elem, attr ) {
			elem = elem[0]
			new Powerange(elem, {
				min: 0,
				max: 10000,
				start: 0,
			});

			scope.$watch('preview.donation', function (newVal,oldVal) {
					var sliderNewVal = newVal;
					var sliderOldVal = oldVal;
					console.log("sliderNewVal : " + sliderNewVal);
					console.log("sliderOldVal : " + sliderOldVal);
					document.getElementById('spinner').value = scope.preview.donation;
			});
		}
	};
})


.directive('progressBar', function () {
	return {
		restrict: 'E',
		template: '<div class="bar"><div class="barFill" style="width:{{...