Robert Marshall 8 anos atrás
pai
commit
799219428e

+ 2 - 1
Controller/Weight.php

@@ -27,6 +27,7 @@ class Weight extends Controller {
 		
 		$reading->Save(Session::GetLoggedInUser());
 		
-		return '{"Weight":'.$reading->Weight.',"BMI":'.$reading->Bmi.',"Fat":'.$reading->Fat.'}';
+		//return '{"Weight":'.$reading->Weight.',"BMI":'.$reading->Bmi.',"Fat":'.$reading->Fat.'}';
+		return json_encode($reading);
 	}
 }

+ 10 - 1
Model/WeightReading.php

@@ -1,5 +1,5 @@
 <?php
-class WeightReading extends DBObject {
+class WeightReading extends DBObject implements JsonSerializable  {
 
 	/**
 	 * @var IUserSettingsRepository
@@ -30,4 +30,13 @@ class WeightReading extends DBObject {
 		$this->UserId=$user->UserId;
 		parent::Save();
 	}
+	
+	public function jsonSerialize() {
+		$output=new stdClass();
+		$output->date=date("c", strtotime($this->Timestamp));
+		$output->weight=(float)$this->Weight;
+		$output->bmi=(float)$this->Bmi;
+		$output->fat=(float)$this->Fat;
+        return $output;
+    }
 }

+ 32 - 109
View/Weight/index.view

@@ -1,3 +1,9 @@
+@Init{
+	$this->RegisterJSFile("services/weightService.js");
+	$this->RegisterJSFile("controllers/weight.js");
+	$this->RegisterJSFile("directives/googleChart.js");
+	$this->RegisterJSFile("directives/scopeInit.js");
+}@
 @Title{Weight}@
 @CSS{
 	.input{
@@ -10,127 +16,44 @@
 		margin-bottom: 5px;
 	}
 }@
-@JavaScript{
-	var chartData;
-	var chart;
-	
-	var chartOptions = {
-		chart: {
-			title: 'Historical Weight Data',
-		},
-		height:400,
-		legend:{
-			position: 'top',
-			alignment: 'start'
-		},
-		series: {
-			0:{axis:'Weight'},
-			2:{axis:'Fat'}
-		},
-		axes:{
-			y:{
-				Weight:{label:'Weight (KG)'},
-				Fat:{label:'Fat %'}
-			}
-		},
-		backgroundColor:'transparent'
-	};
-
-	function DrawChart() {
-		chart.draw(chartData, chartOptions);
-	}
-		
-	function InitChart(){
-		chartData = new google.visualization.DataTable();
-		chartData.addColumn('date', 'Date');
-		chartData.addColumn('number', 'Weight (KG)');
-		chartData.addColumn('number', 'Fat %');
-		chartData.addColumn('number', 'BMI');
-
-		chartData.addRows([
-			<?php
-				foreach ($readings as $r){
-					$dateArray=$r->GetParsedDate();
-					echo '[new Date(',$dateArray['year'],',',$dateArray['month']-1,',',$dateArray['day'],'),',$r->Weight,',',$r->Fat,',',$r->Bmi,'],',PHP_EOL;
-				}
-			?>
-		]);
-
-		//chart = new google.charts.Line(document.getElementById('linechart'));
-		chart = new google.visualization.LineChart(document.getElementById('linechart'));
-		
-		DrawChart();
-	}
-	
-	function AddData(weight, bmi, fat){
-		chartData.addRow([new Date(),weight,fat,bmi]);
-		DrawChart();
-	}
-	
-	function DisableControls(){
-		$("input").attr("disabled","disabled");
-	}
-	
-	function EnableControls(){
-		$("input").removeAttr("disabled");
-	}
-	
-	function AddWeightReading(data){
-		EnableControls();
-		if (data!=""){
-			var reading=JSON.parse(data);
-			AddData(reading.Weight, reading.BMI, reading.Fat);
-			$("form input[type=number]").val("");
-		}
-	}
-	
-	$(function(){
-		$.getScript("https://www.google.com/jsapi",function(){
-			//google.load('visualization', '1.1', {packages: ['line'], callback:function(){
-			google.load('visualization', '1', {packages: ['corechart'], callback:function(){
-				google.setOnLoadCallback(InitChart);
-			}});
-		});
-	});
-}@
 @Body{
-<form ajaxForm action="/weight/add" method="post" onsuccess="AddWeightReading" onpost="DisableControls" autocomplete="off">
+<div ng-controller="weight">
 	<div class="row col-md-4">
 		<div class="col input">
 			<label for="weight">Weight (KG)</label>
-			<input type="number" id="weight" name="weight" step="any" />
+			<input type="number" step="any" ng-model="weight" ng-disabled="submitting" />
 		</div>
 		<div class="col input">
 			<label for="fat">Fat %</label>
-			<input type="number" id="fat" name="fat" step="any" />
+			<input type="number" step="any" ng-model="fat" ng-disabled="submitting" />
 		</div>
 		<div class="col input">
 			<label for="save">Action</label>
-			<input id="save" style="padding:5px" type="submit" value="Save" />
+			<button id="save" style="padding:5px" ng-click="addReading()" ng-disabled="submitting">Save</button>
 		</div>
 	</div>
-</form>
-<div class="row col-lg-1">
-	<div class="col" id="linechart"></div>
-</div>
-<div class="row col-lg-1">
-	<div class="col">
-		<h3>Last 10 readings</h3>
-		<table class="alternatingRows">
-			<tr>
-				<th>Date</th>
-				<th>Weight</th>
-				<th>BMI</th>
-				<th>Fat %</th>
-			</tr>
-			<?php
-				$last10=array_slice($readings, count($readings)-10);
-				$last10=array_reverse($last10);
-				foreach ($last10 as $reading){
-					echo '<tr><td>',date("d/m/y", strtotime($reading->Timestamp)),'</td><td>',$reading->Weight,'</td><td>',round($reading->Bmi,1),'</td><td>',$reading->Fat,'</td></tr>';
-				}
-			?>
-		</table>
+	<div class="row col-lg-1">
+		<google-chart data="readings" headings="headings" legend-position="top" height="400" title="Historical Weight Data"></google-chart>
+	</div>
+	<div class="row col-lg-1">
+		<div class="col">
+			<h3>Last 10 readings</h3>
+			<table class="alternatingRows">
+				<tr>
+					<th>Date</th>
+					<th>Weight</th>
+					<th>BMI</th>
+					<th>Fat %</th>
+				</tr>
+				<tr ng-repeat="reading in tenLatestReadings track by $index">
+					<td>{{reading.date | date:'d/M/yyyy'}}</td>
+					<td>{{reading.weight}}</td>
+					<td>{{reading.bmi}}</td>
+					<td>{{reading.fat}}</td>
+				</tr>
+			</table>
+		</div>
 	</div>
+	<scope-init value="readings"><?=json_encode($readings)?></scope-init>
 </div>
 }@

+ 46 - 2
scripts/controllers/main.js

@@ -1,6 +1,50 @@
 /* global app, angular */
 
-var app = angular.module("robware", ['ngAnimate']);
+var app = angular.module("robware", ['ngAnimate'], function($httpProvider) {
+	/*
+	 * Stolen from: http://stackoverflow.com/questions/19254029/angularjs-http-post-does-not-send-data
+	 * Reason: send payload as form data instead of JSON, bind to controller action parameters
+	 */
+	// Use x-www-form-urlencoded Content-Type
+	$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
+	/**
+	 * The workhorse; converts an object to x-www-form-urlencoded serialization.
+	 * @param {Object} obj
+	 * @return {String}
+	 */
+	var param = function(obj) {
+		var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
+		for (name in obj) {
+			value = obj[name];
+			if (value instanceof Array) {
+				for (i = 0; i < value.length; ++i) {
+					subValue = value[i];
+					fullSubName = name + '[' + i + ']';
+					innerObj = {};
+					innerObj[fullSubName] = subValue;
+					query += param(innerObj) + '&';
+				}
+			}
+			else if (value instanceof Object) {
+				for (subName in value) {
+					subValue = value[subName];
+					fullSubName = name + '[' + subName + ']';
+					innerObj = {};
+					innerObj[fullSubName] = subValue;
+					query += param(innerObj) + '&';
+				}
+			}
+			else if (value !== undefined && value !== null)
+				query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
+		}
+
+		return query.length ? query.substr(0, query.length - 1) : query;
+	};
+	// Override $http service's default transformRequest
+	$httpProvider.defaults.transformRequest = [function(data) {
+		return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
+	}];
+});
 
 app.controller("main", function($scope) {
 	var largeWindowBoundary = 1023;
@@ -8,7 +52,7 @@ app.controller("main", function($scope) {
 	$scope.menuVisible = false;//window.innerWidth > largeWindowBoundary;
 
 	$scope.shouldShowMenu = function() {
-		return $scope.menuVisible || window.innerWidth > 1023;
+		return $scope.menuVisible || window.innerWidth > largeWindowBoundary;
 	};
 
 	$(window).on("resize.doResize", function() {

+ 34 - 0
scripts/controllers/weight.js

@@ -0,0 +1,34 @@
+app.controller("weight", ["$scope", "weightService", function($scope, weightService) {
+		$scope.submitting = false;
+		$scope.weight = null;
+		$scope.fat = null;
+
+		$scope.readings = [];
+		$scope.headings = {weight: "Weight (KG)", fat: "Fat %", bmi: "BMI"};
+
+		$scope.$watch("readings", function() {
+			for (var i = 0; i < $scope.readings.length; i++) {
+				$scope.readings[i].date = new Date($scope.readings[i].date);
+			}
+		});
+
+		$scope.addReading = function() {
+			if (!$scope.weight || !$scope.fat)
+				return;
+
+			$scope.submitting = true;
+			weightService.addWeight($scope.weight, $scope.fat).then(function(data) {
+				$scope.readings = $scope.readings.concat([data]);
+				$scope.submitting = false;
+			});
+		}
+
+		Object.defineProperty($scope, "tenLatestReadings", {
+			get: function() {
+				var temp = $scope.readings.slice(-10);
+				temp.reverse();
+				return temp;
+			}
+		});
+	}]);
+

+ 79 - 0
scripts/directives/googleChart.js

@@ -0,0 +1,79 @@
+/* global google, angular */
+
+app.directive('googleChart', function() {
+	return {
+		restrict: 'E',
+		templateUrl: '/scripts/directives/templates/googleChart.html',
+		scope: {
+			data: '=',
+			headings: '=',
+			height: '='
+		},
+		link: function(scope, element, attributes) {
+			var chartLoaded = false;
+			
+			if (!scope.headings)
+				scope.headings = [];
+			
+			var chartData, chart;
+			var chartOptions = {
+				title: attributes.title,
+				height: scope.height || 400,
+				legend: {
+					position: attributes.legendPosition || 'right'
+				},
+				backgroundColor: attributes.background || 'transparent'
+			};
+
+			function drawChart() {
+				if (!chartLoaded)
+					return;
+
+				var firstRow = scope.data[0];
+				var fields = {};
+				for (var propertyName in firstRow) {
+					var datum = firstRow[propertyName];
+					var type = typeof (datum);
+					if (type === "object")
+						type = datum.constructor.name;
+					fields[propertyName] = {
+						title: scope.headings[propertyName] || propertyName,
+						type: type.toLowerCase()
+					};
+				}
+
+				chartData = new google.visualization.DataTable();
+				angular.forEach(fields, function(value) {
+					chartData.addColumn(value.type, value.title);
+				});
+
+				angular.forEach(scope.data, function(value) {
+					var row = [];
+					angular.forEach(value, function(value2) {
+						row.push(value2);
+					});
+					chartData.addRow(row);
+				});
+
+				chart = new google.visualization.LineChart(element[0]);
+				chart.draw(chartData, chartOptions);
+			}
+
+			$.getScript("https://www.google.com/jsapi", function() {
+				//google.load('visualization', '1.1', {packages: ['line'], callback:function(){
+				google.load('visualization', '1', {
+					packages: ['corechart'],
+					callback: function() {
+						chartLoaded = true;
+						google.setOnLoadCallback(drawChart);
+					}
+				});
+			});
+
+			scope.$watch("data", function(v){
+				drawChart();
+			});
+		}
+	};
+});
+

+ 10 - 0
scripts/directives/scopeInit.js

@@ -0,0 +1,10 @@
+app.directive("scopeInit", function(){
+	return {
+		link:function(scope, element, attributes){
+			var content=element[0].innerHTML.trim();
+			scope[attributes.value]=JSON.parse(content);
+			element.remove();
+		}
+	}
+});
+

+ 1 - 0
scripts/directives/templates/googleChart.html

@@ -0,0 +1 @@
+<div class="mapsContainer">Loading chart...</div>

+ 11 - 0
scripts/services/weightService.js

@@ -0,0 +1,11 @@
+app.service('weightService', function($http) {
+	this.addWeight=function(weight, fat){
+		return $http({
+			method:'post',
+			url:'/weight/add',
+			data:{weight:weight, fat:fat},
+		}).then(function(response){
+			return response.data;
+		});
+	};
+});