Google charts vAxis timespans - angularjs

Hi Guys according to the documentation of google charts a row type can either be :
The type can be one of the following: 'string', 'number', 'boolean',
'date', 'datetime', and 'timeofday'.
https://developers.google.com/chart/interactive/docs/datesandtimes
I need to have timespan type, so I can have vAxis with values 0 hour, 1 hour, 2 hours, ....up to any number. Which will mean between each two is 60 degrees like a timespan, but not 100 degree like numbers.
Is there a way to acheive this? timeofday will not work also when reaching 24 hours it turns it into 00:00

using the option --> hAxis.ticks
combined with object notation for values --> {v: value, f: formattedValue}
you could probably use just about any type (other than 'string')
see the following working snippet for a basic example...
a custom set of hAxis.ticks is built, one tick for each hour, in a 48 hour timespan
google.charts.load('current', {
callback: function () {
drawChart();
window.addEventListener('resize', drawChart, false);
},
packages:['corechart']
});
function drawChart() {
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'Timespan');
dataTable.addColumn('number', 'Y');
var oneHour = (1000 * 60 * 60);
var startDate = new Date();
var endDate = new Date(startDate.getTime() + (oneHour * 24 * 2));
var ticksAxisH = [];
for (var i = startDate.getTime(); i < endDate.getTime(); i = i + oneHour) {
var tickValue = new Date(i);
var tickFormat = (tickValue.getTime() - startDate.getTime()) / oneHour;
var tick = {
v: tickValue,
f: tickFormat + 'h'
};
ticksAxisH.push(tick);
dataTable.addRow([tick, (2 * tickFormat) - 8]);
}
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
chart.draw(dataTable, {
hAxis: {
ticks: ticksAxisH
}
});
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Related

google.visualization.DataTable(JSON.parse(datastring)) is not working

I am using google charts to display some data on the screen and on some button click. The data is loading from webapi call.
To simplyfy the issue, I made the data hardcoded in function itself.
The issue is when I call
google.visualization.DataTable(JSON.parse(datastring))
Message: Table has no columns.
it is returning empty datatable. I also tried with arrayToDatable but no use.
I got error with arrayToDataTable. here is it.
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys
I have create a plunker for it.
http://plnkr.co/edit/pctIqoCWi3LhqxlgdnqM?p=preview
Can anyone have a look and let me know whats wrong with this.
when creating google.visualization.DataTable directly from json,
the json must be in a specific format
see --> Format of the Constructor's JavaScript Literal data Parameter
google.visualization.arrayToDataTable accepts an array of values, not objects...
[["ReportName","ReportTime"],["ABC",48],["XYZ",50]]
if you don't want to change the format of the results from the webapi call,
you'll need to transform the data for the chart
see following working snippet for an example...
google.charts.load('current', {
callback: function () {
var datastring = '{"PerformanceData" : [{"ReportName":"ABC","ReportTime":"48"},{"ReportName":"XYZ","ReportTime":"50"}]}';
var jsonData = JSON.parse(datastring);
var chartData = [];
// load chart data
jsonData.PerformanceData.forEach(function (row, rowIndex) {
// column headings
var columns = Object.keys(row);
if (rowIndex === 0) {
chartData.push(columns);
}
// row values
var chartRow = [];
columns.forEach(function (column, colIndex) {
var chartCell = row[column];
if (colIndex > 0) {
chartCell = parseFloat(chartCell);
}
chartRow.push(chartCell);
});
chartData.push(chartRow);
});
var data = google.visualization.arrayToDataTable(chartData);
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
chart.draw(data, { width: 400, height: 240 });
},
packages:['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
EDIT
once the data table is built, use the group() method to aggregate the data
you can use one of the provided aggregation functions, or provide your own
google.visualization.data.avg
google.visualization.data.count
google.visualization.data.max
google.visualization.data.min
google.visualization.data.sum
see following working snippet...
google.charts.load('current', {
callback: function () {
var datastring = '{"PerformanceData" : [{"ReportName":"ABC","ReportTime":"48"},{"ReportName":"XYZ","ReportTime":"50"},{"ReportName":"ABC","ReportTime":"48"},{"ReportName":"XYZ","ReportTime":"50"},{"ReportName":"ABC","ReportTime":"48"},{"ReportName":"XYZ","ReportTime":"50"},{"ReportName":"ABC","ReportTime":"48"},{"ReportName":"XYZ","ReportTime":"50"}]}';
var jsonData = JSON.parse(datastring);
var chartData = [];
// load chart data
jsonData.PerformanceData.forEach(function (row, rowIndex) {
// column headings
var columns = Object.keys(row);
if (rowIndex === 0) {
chartData.push(columns);
}
// row values
var chartRow = [];
columns.forEach(function (column, colIndex) {
var chartCell = row[column];
if (colIndex > 0) {
chartCell = parseFloat(chartCell);
}
chartRow.push(chartCell);
});
chartData.push(chartRow);
});
var data = google.visualization.arrayToDataTable(chartData);
// group data
var dataGroup = google.visualization.data.group(
data, // data table
[0], // group by column
[{ // aggregation column
column: 1,
type: 'number',
aggregation: google.visualization.data.sum
}]
);
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
// use group data to draw chart
chart.draw(dataGroup, {
pieSliceText: 'value',
width: 400,
height: 240
});
},
packages:['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

split time between start time and end time for user given minutes as input

In my app I have a drop-down for booking an appointment with psychiatrist. Here the user gives the input as minutes, to talk to the doctor,so in drop-down it should look like a normal timings like 9-10 am, 10-11pm so on till 5pm, so the customer can see the available timing to book the appointment. I'm struggling to get this time split. I have done only the conversion from mins to hours and after that Im struck with the time split.Hoping for some help here, or any valid guidance for proceeding to get the time split.
Controller:
myApp.controller('ctrl', function ($scope) {
$scope.calc = function () {
var time = $scope.timeSelect;
if (time < 60) {
var a = (time) + 'm';
} else if (time % 60 == 0) {
var a = (time - time % 60) / 60 + 'h';
} else {
var a = ((time - time % 60) / 60 + 'h' + ' ' + time % 60 + 'm');
}
$scope.result =a;
}
$scope.result='';
});
Whenever you are dealing with dates and times I highly recommend using Momentj.js. Here is an extremely contrived example to demonstrate how you might use Moment.js to set your time periods.
angular.module("app", ["angularMoment"])
.controller("ctrl", function($scope, moment) {
var startingTime = moment().hours(9).minutes(0);
var endingTime = moment().hours(17).minutes(0);
$scope.selectedTimeslot = "";
$scope.intervals = [15, 30, 45, 60, 90, 120];
$scope.interval = 60;
$scope.timeslots = [];
$scope.setTimeSlots = function() {
$scope.timeslots = [];
var currentStartTime = angular.copy(startingTime); // use copy to avoid reference issues since we're dealing with objects and not primitives
while (currentStartTime < endingTime) {
$scope.timeslots.push(currentStartTime.format("h:mm") + " - " + currentStartTime.add($scope.interval, "minute").format("h:mm"));
}
}
$scope.setTimeSlots();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.16.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-moment/1.0.0/angular-moment.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
Interval in minutes: <select ng-model="interval" ng-change="setTimeSlots()" ng-options="i for i in intervals"></select>
<br/>Timeslots:
<select ng-model="selectedTimeslot" ng-options="slot for slot in timeslots">
<option value="">Please select</option>
</select><br/><br/>
Selected timeslot: {{selectedTimeslot}}
</div>
This might work for you:
function createTimeSlots(startHour, endHour, interval) {
if (!startHour) {
endHour = 8;
}
if (!endHour) {
endHour = 20;
}
var timeSlots = [],
dateTime = new Date(),
timeStr = '';
dateTime.setHours(startHour, 0, 0, 0);
while (new Date(dateTime.getTime() + interval * 60000).getHours() < endHour) {
timeStr = dateTime.getHours() + ':' + dateTime.getMinutes();
timeStr += '-';
dateTime = new Date(dateTime.getTime() + interval * 60000);
timeStr += dateTime.getHours() + ':' + dateTime.getMinutes();
timeSlots.push(timeStr);
}
return timeSlots;
}
console.log(createTimeSlots(9, 18, 45));
"9:0-9:45",
"9:45-10:30",
"10:30-11:15",
"11:15-12:0",
"12:0-12:45",
"12:45-13:30",
"13:30-14:15",
"14:15-15:0",
"15:0-15:45",
"15:45-16:30",
"16:30-17:15"
You might wanna add some 0 padding on the getHours() and getMinutes() so that 1:0 will be printed as 01:00.
From the question I get a rough idea of what you need, so I hope this helps you on your way. First, I would make a separate function to convert a time (in number of minutes from midnight, as an easy example) to a string, like this:
function timeToString(time) {
if (time < 12 * 60)
return (time - time % 60) / 60 + ":" + time % 60 + "am";
else return (time - time % 60 - 12*60) / 60 + ":" + time % 60 + "pm";
}
Then the array to fill the drop-down can be created like this:
var times = [];
for (t = startHour * 60; t < endHour * 60; t += duration) {
times[times.length] = timeToString(t) + ' - ' + timeToString(t + duration);
}

If element exists by data attribute

I have a number of spans being created with ng-repeat:
<div class="row" id="year-1">
<span class="event" ng-repeat="(key, event) in events" event data-start={{event.date_start}} data-end={{event.date_end}} data-key={{key}} data-type={{event.role}}>
{{event.title}} - {{event.date_start}}
</span>
</div>
I have a directive for event which does a number of things to manipulate each span created accordingly. One of the things is to check is there are other spans with data-type="X".
In my directive, if I do the following, I get all the span's with class 'event':
var parentid = angular.element(document.getElementById('year-1'));
var typeExists = parentid[0].querySelectorAll('.event')[0];
But if I try to narrow it down to data-type="X" such as the following, I get undefined.
var typeExists = parentid[0].querySelectorAll('.event[data-type="' + attr.type + '"]')[0];
Am I overlooking something? Full directive:
angular.module("app").directive("event", function() {
return {
link: function(scope, element, attr) {
var getStart = attr.start.split('-'),
getEnd = attr.end.split('-'),
getKey = attr.key;
getHeight = element[0].offsetHeight;
var parentid = angular.element(document.getElementById('year-1')),
backgroundParent = angular.element(document.getElementsByClassName('year-current'));
// get the month event starts with
var monthStart = angular.element(document.querySelectorAll('[data-location="' + getStart[1] + '"]'));
// get the month event ends with
var monthEnd = angular.element(document.querySelectorAll('[data-location="' + getEnd[1] + '"]'));
// how many events do we have
var eventcount = angular.element(document.getElementsByClassName('event'));
// get width of events container
var eventsContainer = angular.element(document.getElementById('events'));
// does this type exist already, if so get its top
var typeExists = parentid[0].querySelectorAll('.event')[0];
console.log(typeExists);
if(monthStart.length > 0) {
// how many days in start month
var daysStart = getDaysInMonth(getStart[1], getStart[0]),
daysStartPercent = (getStart[2] / daysStart.length);
// how many days in end month
var daysEnd = getDaysInMonth(getEnd[1], getEnd[0]),
daysEndPercent = (getEnd[2] / daysEnd.length);
// determine left starting %
var elementLeft = ((monthStart[0].offsetLeft + (monthStart[0].clientWidth * daysStartPercent)) / eventsContainer[0].clientWidth) * 100;
// determine width in %
var elementRight = ((monthEnd[0].offsetLeft + (monthEnd[0].clientWidth * daysEndPercent)) / eventsContainer[0].clientWidth) * 100;
var width = (elementRight - elementLeft);
// get the background color for this role
var background = angular.element(document.querySelector('.role[data-type="' + attr.type + '"]'))[0].getAttribute('data-background');
element.css({
'left': elementLeft + '%',
'top' : parentid[0].offsetHeight + 'px',
'width': width + '%',
'background': background
});
element.addClass('stretchRight');
parentid.css({'height': parentid[0].offsetHeight + getHeight + 'px'});
backgroundParent.css({'height': parentid[0].offsetHeight + getHeight + 'px'});
} else {
element.css({ 'display': 'none' });
}
}
}
});
I was able to recreate the problem you saw and found that you need to change your line from this:
var typeExists = parentid[0].querySelectorAll('.event[data-type="' + attr.type + '"]')[0];
to this:
var typeExists = parentid.querySelectorAll('.event[data-type="' + attr.type + '"]')[0];
Remove the [0] from your parentid reference because you only have a single element with ID of year-1 which means it's not an array.

Dygraphs and Angular

I am trying to put together Dygraphs charting library and AngularJS. I have downloaded https://github.com/cdjackson/angular-dygraphs and following the provided example. I was able to have it working with my code. My next step is to assign data to a graph in a callback after data is returned by the server. That is not working well. Have someone any sample of setting up data after it's been returned from the server?
Thanks
<div id="graphdiv" class="col-md-8">
<ng-dygraphs
options="graph.options"
legend="graph.legend"
data="graph.data"/>
</div>
charts.controller('dygraphController', function($scope, chartsService) {
$scope.graph = {
data: [
],
options: {
labels: ["x", "A", "B"]
},
legend: {
series: {
A: {
label: "Series A"
},
B: {
label: "Series B",
format: 3
}
}
}
};
// This function is a callback
function result(data) {
var base_time = Date.parse("2008/07/01");
var num = 24 * 0.25 * 365;
for (var i = 0; i < num; i++) {
$scope.graph.data.push([new Date(base_time + i * 3600 * 1000),
i + 50 * (i % 60), // line
i * (num - i) * 4.0 / num // parabola
]);
};
};
});
I am getting the following in the Console:
Can't plot empty data set
Heights null 320 320 320 320 0 0
Position [object Object]

Convert birthday to age in angularjs

I want to display age of all my users to grid. I am reading data from facebook.I am not storing it at anywhere.
i am displaying date like :
{{ friend.birthday }}
How can i display age instead of displaying birthday.
if it is possible to create filters than how to create filter and how to apply it.
You can implement a function:
Controller:
$scope.calculateAge = function calculateAge(birthday) { // birthday is a date
var ageDifMs = Date.now() - birthday.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970);
}
HTML
{{ calculateAge(friend.birthday) }}
Or a filter:
app.filter('ageFilter', function() {
function calculateAge(birthday) { // birthday is a date
var ageDifMs = Date.now() - birthday.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970);
}
return function(birthdate) {
return calculateAge(birthdate);
};
});
HTML
{{ friend.birthday | ageFilter }}
Age algorithm taken from this SO answer.
[EDIT] If the age is less than 1 year, and you want to show months, you can modify the ageFilter to calculate the month difference:
app.filter('ageFilter', function() {
function calculateAge(birthday) { // birthday is a date
var ageDifMs = Date.now() - birthday.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970);
}
function monthDiff(d1, d2) {
if (d1 < d2){
var months = d2.getMonth() - d1.getMonth();
return months <= 0 ? 0 : months;
}
return 0;
}
return function(birthdate) {
var age = calculateAge(birthdate);
if (age == 0)
return monthDiff(birthdate, new Date()) + ' months';
return age;
};
});
Demo Plunker - Age Function
Demo Plunker - Age Filter
Demo Plunker - Age Filter with Months < 1 year
If you're value is just for example "05/01/2016". This will be a useful code to convert the date to birthday.
AngularJS
app.filter('ageFilter', function(){
return function(birthday){
var birthday = new Date(birthday);
var today = new Date();
var age = ((today - birthday) / (31557600000));
var age = Math.floor( age );
return age;
}
});
HTML
{{ relationTypePreDefined.birthdate | ageFilter }}
By the way I used this solution to convert a date coming from a jquery datepicker input to age.
If you are using momentjs. Then you can create filter simply by using this snippet
var now = "04/09/2013 15:00:00";
var then = "04/09/2013 14:20:30";
moment.utc(moment(now,"DD/MM/YYYY HH:mm:ss").diff(moment(then,"DD/MM/YYYY HH:mm:ss"))).format("HH:mm:ss")
Idk why I can never reply to people, says I need more rep but to rep I need to comment.. whatever.
In response to #Dean Christian Armada's, I kept getting an error regarding the filter. But changing to the following seems to work fine so I do appreciate it!
$scope.getAge = function(birthday){
var birthday = new Date(birthday);
var today = new Date();
var age = ((today - birthday) / (31557600000));
var age = Math.floor( age );
return age;
}
And for the HMTL
{{ getAge(birthdate) }}

Resources