I would like to display year view of events in fullcalendar v4. Year view has been added to full calendar until version 2.2.7. Later version does not have this. So, I decided to create year view using custom view . But I did not get where should I add the html part that display year view in the way we want. This is how I create a view. But listYear plugin able to display all events in the year in list form. I would like to display events in calendar display all month on one view. Is it possible to add our own html table to render in calendar?
views: {
Year: {
type : 'listYear',
duration : {
months: 12
},
start :year+'-01-01',
end :(year+1)+'-01-01',
buttonText:'year'
}
}
document.addEventListener('DOMContentLoaded', function() {
var year =<?php echo $year;?>;
var event ='<?php echo json_encode($events);?>';
event=JSON.parse(event);
//console.log(event);
//create event Array
events_array=[];
for(i=0;i<event.length;i++)
{
//parameter for event Array-https://fullcalendar.io/docs/event-object
start_array=event[i]['start'].split(" ");
end_array=event[i]['end'].split(" ");
if(start_array[1]==='00:00:00')
{
start =start_array[0];
}
else
{
start =event[i]['start'];
}
if(end_array[1]==='00:00:00')
{
end =end_array[0];
}
else
{
end =event[i]['end'];
}
object_data={
id :event[i]['id'],
title :event[i]['remark']+'-'+event[i]['title'],
start :start,
end :end,
color :event[i]['color'],
}
events_array.push(object_data);
}
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
plugins: ['dayGrid','list'],
header:
{
left : 'prev,next today',
center : 'title',
//version 2.2.7 able to show year view.Year view has been implemented until version 2.2.7.
//But , we are using 4 to use valid range (limiting the duration).Valid range available from 3.3.0
right : 'Year,dayGridMonth,dayGridWeek,dayGridDay,listMonth'
},
views: {
Year: {
/* type: 'timelineYear',*/
type: 'listYear',
duration: { months: 12
/*weeks:1*/ },
start:year+'-01-01',
end: (year+1)+'-01-01',
/*intervalStart: $.fullCalendar.moment(year+'-01-01'),
intervalEnd: $.fullCalendar.moment((year+1)+'-01-01'),*/
buttonText: 'year'
}
},
defaultDate: year+'-01-01',
//set the year range limit - fullcalendar.min.js v3.3.0 & above support this
validRange: {
start : year+'-01-01',
end : (year+1)+'-01-01'
},
defaultView : 'dayGridMonth',
editable : false,//disable drag
events : events_array,
//from [https://stackoverflow.com/questions/45698134/dayrender-not-properly-working-in-fullcalendar-on-given-dates]
dayRender: function (date, cell)
{
var disabledDates = ["2016-02-10", "2016-02-15"];
//$.inArray returns the index of the element in the array, not a boolean indicating if the item exists in the array. If the element was not found, -1 will be returned.
/*if ($.inArray(date.format("YYYY-MM-DD"), disabledDates) > -1)
{
cell.css("background-color", "green");
}
*/
}
});
calendar.render();
});
Thanks in advance
My aim is to use version 3 and above since it support the valid range function and version 3 custom view is quite understandable. So, I had use version 3 to do year view. This year view limited for only one year that consist of 12 months. Declare custom view in fullcalendar instantiation.
views:
{
CustomView:
{
type: 'custom',
}
}
Declare a button year in header for custom year view . Then in customButtons:, define what happen when user click this button.
customButtons:
{
year:
{
text:'year',
click: function()
{
$('#calendar').fullCalendar('changeView', 'CustomView');
}
}
}
Then, get reference(FC) to fullcalendar's root namespace and class(View) that all views inherit from.
var FC =$.fullCalendar;
var View =FC.View;
Build the view inside render: function() of our class CustomView. Create table containing 12 field (3 columns and 4 rows) and append to fc-view. Then ,arrange each event in object with required informations and add to events_array.events_array contain objects of events. Get next 12 months start from which period month start in array months. Then, loop in months array and instantiate fullcalendar view in each field in that table created by using the id. Id is value from months(ex:'january_2019'). Set the duration one month and pass in the events array. So, each field show the view for one month together with the events. 12 fields shows view for 12 months.
CustomView = View.extend(
{
render: function()
{ }}
Register our class with the view system.
FC.views.custom = CustomView;
<?php
require_once('bdd.php');
// information from sql
// $event_2 is events array
// $event_3 are array of earliest event start and latest event end date, chosen period start and end date,
?>
<html>
<head>
<style>
#container
{
width: 100%;
position: relative;
}
#calendar
{
width: 900px; /*can be in percentage also.*/
height: auto;
margin: 0 auto;
padding: 10px;
position: relative;
}
td.fc-sun
{
background-color: #535450 !important;
}
</style>
<script src="js/jquery.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src='js/moment.min.js'></script>
<script src='v_3/fullcalendar.min.js'></script>
<link rel="stylesheet" href="v_3/fullcalendar.min.css" />
<script>
$(document).ready(function()
{
var event ='<?php echo json_encode($events_2);?>';
event =JSON.parse(event);
limit ='<?php echo json_encode($events_3);?>';
limit =JSON.parse(limit);
events_array=[];
for(i=0;i<event.length;i++)
{
//parameter for event Array-https://fullcalendar.io/docs/event-object
start_array=event[i]['start'].split(" ");
end_array=event[i]['end'].split(" ");
if(start_array[1]==='00:00:00')
{
start =start_array[0];
}
else
{
start =event[i]['start'];
}
if(end_array[1]==='00:00:00')
{
end =end_array[0];
}
else
{
end =event[i]['end'];
}
console.log(start+'=>'+end);
object_data=
{
id :event[i]['id'],
title :event[i]['remark']+'-'+event[i]['title'],
start :start,
end :end,
color :event[i]['color'],
}
events_array.push(object_data);
}
$('#calendar').fullCalendar
({
defaultDate: limit[0]['earliest'],
validRange: {
start : limit[0]['earliest'],
end : limit[0]['final']
},
header :
{
left :'prev,next,today',
center :'title',
right :'year,agendaWeek,month'
},
events : events_array,
customButtons:
{
year:
{
text:'year',
click: function()
{
$('#calendar').fullCalendar('changeView', 'CustomView');
}
}
},
views:
{
CustomView:
{
type: 'custom',
}
}
})
var view = $('#calendar').fullCalendar('getView');
//custom view:
var FC = $.fullCalendar; //a reference to FullCalendar's root namespace
var View =FC.View; //the class that all views must inherit from
var CustomView; //our subclass
start_year =limit[0]['earliest'].split("-")[0];
end_year =limit[0]['final'].split("-")[0];
start_month =parseInt(limit[0]['fye_start'].split("-")[1]);
CustomView = View.extend(
{
render: function()
{
$('.fc-prev-button').addClass('fc-state-disabled');
$('.fc-next-button').addClass('fc-state-disabled');
//change the title
document.getElementsByClassName("fc-center")[0].getElementsByTagName('h2')[0].innerHTML = start_year;
//https://stackoverflow.com/questions/10832179/result-of-getting-next-12-months-in-javascript-is-messed-up
var months =getNext12MonthNamesWithYear(limit[0]['fye_start']);
var table ='<table align="center" style="width:100%">';
var m=0;
for(i=1;i<=4;i++)
{
table+='<tr>';
for(j=1;j<=3;j++)
{
table+='<td height="100"><div id="'+months[m]+'"></div></td>';
m++;
}
table+='</tr>';
}
table+='</table>';
$('.fc-view').append(table);
for(n=0;n<months.length;n++)
{
year =months[n].split("_")[1];
month=months[n].split("_")[0];
//https://stackoverflow.com/questions/13566552/easiest-way-to-convert-month-name-to-month-number-in-js-jan-01/27805696
month=getMonthFromString(month);//convert month string to month no
//month compulsory to have 2 digit
if(month>=10)
{
c=month;
}
else
{
c='0'+month;
}
$('#'+months[n]).fullCalendar
(
{
header:
{
left: '',
center: 'title',
right: ''
},
events : events_array,
defaultDate : year+'-'+(c)+'-01',
//set the year range limit - fullcalendar.min.js v3.3.1 & above support this
defaultView:'month',
duration:
{
months: 1
}
})
}
},
}
)
FC.views.custom = CustomView; // register our class with the view system*/
})
function getNext12MonthNamesWithYear(date)
{
var now = new Date(date);
var month = now.getMonth();
var year = now.getFullYear();
var names = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
var res = [];
for (var i = 0; i < 12; ++i)
{
res.push(names[month] + '_' + year);
if (++month === 12)
{
month = 0;
++year;
}
}
return res;
}
function getMonthFromString(mon)
{
return new Date(Date.parse(mon +" 1, 2012")).getMonth()+1
}
</script>
</head>
<body>
<div id='calendar'></div>
</body>
Looking forward for better solution.
I need to change a dataTable to add a dropbox on top of it (not in the header) to filter it.
I have this column with dates like dd/mm/yyyy and I the year range does not go from 01/01/N to 31/12/N. It goes from 01/11/N to 31/10/N+1 and this rule will be used to calculate the filters available.
In case I have only this to lines :
-------------------------------------------------------
| date_header | header 2 | header 3 | header 4 |
|-------------------------------------------------------|
| 01/05/2013 | abc | qwe | xyz |
|-------------------------------------------------------|
| 05/11/2018 | hdf | ydb | lot |
-------------------------------------------------------
I should get the following results on de dropbox (respecting the rule I talked about) :
<2012/2013>
and
<2018/2019>
So, firstly, I need this dropbox to read every values presents on this column and calculate the filter values.
And then, filter the table using the range selected.
-- EDIT1 --
This is my initilialization script:
$element.DataTable({
"ajax": {
"url": this._tableDatasource,
"dataSrc": ""
},
"sDom": 'ltipr',
"bLengthChange": false,
"paging": this._detautNombreElementsPerPage > 0,
"pagingType": "full_numbers",
"iDisplayLength": this._detautNombreElementsPerPage,
"order": [[ this._defautColumnTrie, this._defautTypeTrie ]],
"columns": this._columns,
"columnDefs" : this._columnsProperties,
"fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
if ($(nRow).hasClass('even')) {
$(nRow).addClass("alt");
} else {
if ($(nRow).hasClass('alt')) {
$(nRow).removeClass("alt");
}
}
},
"fnDrawCallback": function() {
var pageCount = Math.ceil((this.fnSettings().fnRecordsDisplay()) / this.fnSettings()._iDisplayLength);
if (pageCount > 1) {
$('.dataTables_paginate').show();
} else {
$('.dataTables_paginate').hide();
}
},
"language": {
"sProcessing": "Chargement en cours...",
"sLengthMenu": "Montrer _MENU_ registres",
"sZeroRecords": "Aucun résultat n'a été trouvé",
"sEmptyTable": "Aucune donnée disponible pour ce tableau",
"sInfo": "_TOTAL_ éléments trouvés, affichage de _START_ à _END_",
"sInfoEmpty": "0 éléments trouvés, affichage de 0 à 0",
"sInfoFiltered": "(filtré au total de _MAX_ registres)",
"sInfoPostFix": "",
"sSearch": "Chercher:",
"sUrl": "",
"sInfoThousands": ",",
"sLoadingRecords": "Chargement en cours...",
"oPaginate": {
"sFirst": "Première page",
"sLast": "Dernière page",
"sNext": "Page suivante",
"sPrevious": "Page précédente"
}
},
"initComplete": function() {
if ($(this).attr('id') == "tableIndisponibilitesPassees") {
var dates = $('#tableIndisponibilitesPassees tr td:first-child').toArray();
populate_dropdown(dates);
//$('#tableIndisponibilitesPassees').dataTable().fnClearTable();
//$('#tableIndisponibilitesPassees').dataTable().fnFilter("20/10/2015 08:00:00").draw();
set_handler();
}
}
});
I had to add the initComplete to populate the table.
This is my populate dropdown :
function populate_dropdown(dates) {
// make an empty array variable to hold the list of saisons
var saisons = [];
// loop through the dates
for (var i = 0; i < dates.length; i++) {
var year = Number($(dates[i]).html().split(' ')[0].split('/')[2]);
var month = Number($(dates[i]).html().split(' ')[0].split('/')[1] - 1);
var day = Number($(dates[i]).html().split(' ')[0].split('/')[0]);
var datePFHA = new Date(year, month, day);
var dateDebutSaison = new Date(year, 10, 1);
// now let's calculate the season
var saison;
if (datePFHA < dateDebutSaison) {
saison = Number(year-1) + "/" + year;
} else {
saison = year + "/" + Number(year+1);
}
// now let's add that saison to the seasons array (if it's not already in the array!)
if ($.inArray(saison, saisons) == -1) {
saisons.push(saison);
}
}
// now that we're done looping through and building the seasons list, let's sort the array
saisons.sort();
// make a variable to hold all the <option> fields for the select dropdown
var options = "";
// loop through the years and make the options fields
$.each(saisons, function(key,value) {
options += "<option> Saison " + value + "</option>";
});
// take the new options string that we've built and put it inside the <select> dropdown
$('#filtre_saison').append(options);
}
And now I'm trying to set the handler like this :
function set_handler(dataTable) {
console.log("set_handler");
var filtre = $('#filtre_saison').on('change', function() {
// when someone changes the filter, get the beginning and ending of the season
var yearsSaison = $("#filtre_saison").val().split(' ')[1];
var debutSaison = new Date(yearsSaison.split('/')[0],10,01);
var finSaison = new Date(debutSaison.getFullYear() + 1, debutSaison.getMonth(), debutSaison.getDate());
console.log($('#tableIndisponibilitesPassees'));
console.log($('#tableIndisponibilitesPassees').dataTable());
console.log($('#tableIndisponibilitesPassees').dataTable().fnFilter("20/10/2015 08:00:00"));
console.log($('#tableIndisponibilitesPassees').dataTable().fnFilter("20/10/2015 08:00:00").draw());
$('#tableIndisponibilitesPassees').dataTable().fnFilter("20/10/2015 08:00:00").draw();
//$(dataTable).search("20/10/2015 08:00:00").draw();
//filter_table(debutSaison, finSaison);
});
}
I've tryed the search method on dataTable but it doesn't work. Return an error saying that search is not a function.
I've tryed with the fnFilter but now it returns an error in the draw function saying :
Cannot read property 'draw' of undefined
I've checked and after the fnFilter function, is returning undefined.
--- EDIT 2 ---
Almost forgot. This is my html code :
<select name="filtre_saison" id="filtre_saison">
</select>
Appreciate your help
OK, without any specifics of how the table is built, etc., a general way to go about this might be to use javascript/jQuery to loop through the date fields and build the year list. Then from that year list, you can populate a dropdown. Then you add a change handler to the dropdown and use javascript to show/hide elements based on the selected year.
Here's a quick and dirty example, which is assuming a couple things:
The fields that hold the date has a class (in my case, 'date_element')
The dates in the fields are of a consistent format
You can run the working snippet below to see it in action. And I added lots of code comments to explain what certain lines are doing.
Hope it helps! (and I hope I didn't just do your homework for you...).
// trigger the function that populates the dropdown when the page has finished loading, and then add a change handler on the dropdown
$(document).ready(function() {
populate_dropdown();
set_handler();
});
function populate_dropdown() {
// make an empty array variable to hold the list of years
var years = [];
// loop through the date_header fields (assuming that the elements have a class of date_element)
$('.date_element').each(function(){
// $(this) inside the each function refers to the <td class='date_element'> element
var this_date = $(this).html();
// get the year from the date. It would be better to use actual Date functions, but if the
// dates are consistent, we can just break them apart on the / characters and grab the last value
var this_year = Number(this_date.split('/')[2]);
// now let's add that year to the years array (if it's not already in the array!)
if ($.inArray(this_year, years) == -1) {
years.push(this_year)
}
});
// now that we're done looping through and building the year list, let's sort the array
years.sort();
// make a variable to hold all the <option> fields for the select dropdown
var options = "";
// loop through the years and make the options fields
$.each(years, function(key,value) {
options += "<option>" + value + "/" + Number(value+1) + "</option>";
});
// take the new options string that we've built and put it inside the <select> dropdown
$('#year_filter').append(options)
}
function set_handler() {
$('#year_filter').change(function() {
// when someone changes the filter, get the value of the first year
var selected_year = $("#year_filter").val().split('/')[0];
// make sure it's a number
if (!isNaN(selected_year)) {
filter_table(selected_year);
} else {
show_all_rows();
}
});
}
function filter_table(selected_year) {
//loop through the table rows, show ones that have the year and hide ones that don't have it
$('.date_element').each(function() {
if ($(this).html().indexOf(selected_year) == -1) {
//this row doesn't contain that year, let's hide the whole <tr>
$(this).parent().hide();
} else {
$(this).parent().show()
}
});
}
function show_all_rows() {
$('.date_element').each(function() {
$(this).parent().show();
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<form>
<select id="year_filter" name="year_filter"><option>Filter By Year</option></select>
</form>
<table>
<tr>
<th>date_header</th>
<th>header 1</th>
<th>header 2</th>
<th>header 3</th>
</tr>
<tr>
<td class='date_element'>01/05/2013</td>
<td>abc</td>
<td>qwe</td>
<td>xyz</td>
</tr>
<tr>
<td class='date_element'>05/11/2018</td>
<td>hdf</td>
<td>ydb</td>
<td>lot</td>
</tr>
</table>
Found the solution :
HTML :
<select name="filtre_saison" id="filtre_saison">
</select>
<table>
...
</table>
Javascript code :
"initComplete": function() {
if ($(this).attr('id') == "tableBuildings") {
var dates = $('#tableBuildings tr td:first-child').toArray();
populate_dropdown(dates);
$.fn.dataTable.ext.search.push( function( settings, data, dataIndex, rowData, counter ) {
if (settings.nTable.id === 'tableBuildings') {
//Calculation of beggining and ending of the room
var yearsBuildig = $("#filtre_saison").val().split(' ')[1];
var initialDate = new Date(yearsBuildig.split('/')[0],10,01);
var endDate = new Date(initialDate.getFullYear() + 1, initialDate.getMonth(), initialDate.getDate());
//Calculation of the Date object of the PFHA
var year = Number(rowData.initialDate.display.split(' ')[0].split('/')[2]);
var month = Number(rowData.initialDate.display.split(' ')[0].split('/')[1] - 1);
var day = Number(rowData.initialDate.display.split(' ')[0].split('/')[0]);
var hours = Number(rowData.initialDate.display.split(' ')[1].split(':')[2]);
var buildingDate = new Date(year, month, day);
if (buildingDate >= initialDate && buildingDate < endDate) {
return true;
} else {
//this row doesn't contain that year, let's hide the whole <tr>
return false;
}
} else {
return true;
}
});
$('#filtre_saison').on('change', function() {
table.draw();
});
table.draw();
}
}
function populate_dropdown(dates) {
// make an empty array variable to hold the list of building
var building = [];
// loop through the dates
for (var i = 0; i < dates.length; i++) {
var year = Number($(dates[i]).html().split(' ')[0].split('/')[2]);
var month = Number($(dates[i]).html().split(' ')[0].split('/')[1] - 1);
var day = Number($(dates[i]).html().split(' ')[0].split('/')[0]);
var buildingDate = new Date(year, month, day);
var initialDateRoom = new Date(year, 10, 1);
// now let's calculate the room
var room;
if (buildingDate < initialDateRoom) {
room = Number(year-1) + "/" + year;
} else {
room = year + "/" + Number(year+1);
}
// now let's add that room to the building array (if it's not already in the array!)
if ($.inArray(room, building) == -1) {
building.push(room);
}
}
// now that we're done looping through and building the building list, let's sort the array
building.sort();
// make a variable to hold all the <option> fields for the select dropdown
var options = "";
// loop through the years and make the options fields
$.each(building, function(key,value) {
options += "<option> room " + value + "</option>";
});
// take the new options string that we've built and put it inside the <select> dropdown
$('#filtre_saison').append(options);
}
Just added a function to populate the dropbox in the initComplete property to populate it only after the table is fully initialized (all the row values charged because I need to have different values depending on the values present on first column).
Then, I use the method to build my custom filter and then just redraw the table and Puff!
PS :
Needed to append this if :
if (settings.nTable.id === 'tableBuildings')
to check if it's the specific table I want because I have multiple tables on this page and the filter is applied to every datatable tables present in the page.
Thankyou #Reverend Pete
I wrote a simple code to manipulate a query result to a bidimensional array (matrix) for a google chart datatable.
I'm getting nut on this strange array.push behaviour: when I push a new row in the matrix this method add the row, but also change the value of all the previus rows!
this is the code:
<script src="https://www.gstatic.com/charts/loader.js" type="text/javascript"></script>
<script type="text/javascript">
query is the matrix from the query
var qry = [[{label:'Montly', type: 'string'}, {label:'Model', type: 'string'}, {label:'total', type: 'number'}], ['12-2017','California T',parseFloat(13+13)], ['12-2017','458 Speciale',parseFloat(3+2)], ['12-2017','GTC4Lusso',parseFloat(2+6)], ['12-2017','458 Spider',parseFloat(0+1)], ['12-2017','GTC4Lusso T',parseFloat(2+0)], ['12-2017','California',parseFloat(0+2)], ['12-2017','488 Spider',parseFloat(16+7)], ['12-2017','FF',parseFloat(1+3)], ['12-2017','488 GTB',parseFloat(17+10)], ['12-2017','F12berlinetta',parseFloat(3+4)], ['12-2017','458 Italia',parseFloat(0+3)], ['12-2017','F12tdf',parseFloat(1+21)], ['12-2017','LaFerrari Aperta',parseFloat(0+1)], ['12-2017','812 Superfast',parseFloat(2+3)], ['12-2017','Portofino',parseFloat(1+1)], ['11-2017','458 Spider',parseFloat(2+4)], ['11-2017','GTC4Lusso',parseFloat(4+34)], ['11-2017','California T',parseFloat(8+15)], ['11-2017','488 GTB',parseFloat(9+12)], ['11-2017','LaFerrari',parseFloat(0+1)], ['11-2017','458 Speciale',parseFloat(2+2)], ['11-2017','GTC4Lusso T',parseFloat(0+1)], ['11-2017','FF',parseFloat(1+4)], ['11-2017','812 Superfast',parseFloat(3+1)], ['11-2017','California',parseFloat(0+3)], ['11-2017','488 Spider',parseFloat(11+8)], ['11-2017','458 Italia',parseFloat(1+5)], ['11-2017','F12tdf',parseFloat(0+1)], ['11-2017','Portofino',parseFloat(0+1)], ['11-2017','F12berlinetta',parseFloat(3+4)], ['10-2017','458 Italia',parseFloat(0+4)], ['10-2017','California T',parseFloat(9+18)], ['10-2017','California',parseFloat(0+8)], ['10-2017','812 Superfast',parseFloat(1+2)], ['10-2017','F12tdf',parseFloat(1+2)], ['10-2017','Portofino',parseFloat(2+0)], ['10-2017','488 GTB',parseFloat(8+14)], ['10-2017','FF',parseFloat(0+3)], ['10-2017','458 Spider',parseFloat(1+3)], ['10-2017','LaFerrari Aperta',parseFloat(0+1)], ['10-2017','F12berlinetta',parseFloat(5+8)], ['10-2017','458 Speciale',parseFloat(3+2)], ['10-2017','488 Spider',parseFloat(9+7)], ['10-2017','GTC4Lusso',parseFloat(2+4)], ['9-2017','California',parseFloat(0+4)], ['9-2017','458 Speciale Aperta',parseFloat(1+0)], ['9-2017','FF',parseFloat(3+6)], ['9-2017','812 Superfast',parseFloat(2+1)], ['9-2017','458 Italia',parseFloat(1+3)], ['9-2017','GTC4Lusso',parseFloat(2+6)], ['9-2017','F12tdf',parseFloat(1+0)], ['9-2017','LaFerrari',parseFloat(0+1)], ['9-2017','488 GTB',parseFloat(18+13)], ['9-2017','458 Spider',parseFloat(3+2)], ['9-2017','F12berlinetta',parseFloat(4+10)], ['9-2017','458 Speciale',parseFloat(5+1)], ['9-2017','California T',parseFloat(23+37)], ['9-2017','488 Spider',parseFloat(11+14)], ['8-2017','FF',parseFloat(0+1)], ['8-2017','458 Spider',parseFloat(0+1)], ['8-2017','F12berlinetta',parseFloat(0+3)], ['8-2017','GTC4Lusso',parseFloat(0+6)], ['8-2017','488 GTB',parseFloat(0+1)], ['8-2017','California T',parseFloat(0+2)], ['8-2017','458 Italia',parseFloat(0+2)], ['8-2017','California',parseFloat(0+2)], ['7-2017','488 GTB',parseFloat(9+14)], ['7-2017','458 Speciale',parseFloat(1+4)], ['7-2017','California T',parseFloat(14+20)], ['7-2017','GTC4Lusso',parseFloat(1+7)], ['7-2017','California',parseFloat(1+6)], ['7-2017','458 Italia',parseFloat(1+4)], ['7-2017','458 Speciale Aperta',parseFloat(1+0)], ['7-2017','F12berlinetta',parseFloat(3+7)], ['7-2017','FF',parseFloat(2+3)], ['7-2017','458 Spider',parseFloat(1+3)], ['7-2017','488 Spider',parseFloat(10+10)], ['7-2017','F12tdf',parseFloat(0+1)], ['6-2017','488 GTB',parseFloat(7+23)], ['6-2017','458 Italia',parseFloat(2+2)], ['6-2017','812 Superfast',parseFloat(1+0)], ['6-2017','FF',parseFloat(0+3)], ['6-2017','GTC4Lusso',parseFloat(2+16)], ['6-2017','458 Spider',parseFloat(3+6)], ['6-2017','F12tdf',parseFloat(0+2)], ['6-2017','LaFerrari',parseFloat(0+1)], ['6-2017','F12berlinetta',parseFloat(4+9)], ['6-2017','458 Speciale',parseFloat(2+3)], ['6-2017','California T',parseFloat(16+18)], ['6-2017','488 Spider',parseFloat(11+14)], ['5-2017','458 Speciale',parseFloat(4+4)], ['5-2017','458 Italia',parseFloat(0+2)], ['5-2017','F12berlinetta',parseFloat(2+5)], ['5-2017','F12tdf',parseFloat(0+3)], ['5-2017','FF',parseFloat(0+1)], ['5-2017','488 GTB',parseFloat(8+13)], ['5-2017','458 Spider',parseFloat(0+2)], ['5-2017','GTC4Lusso',parseFloat(0+6)], ['5-2017','488 Spider',parseFloat(6+12)], ['5-2017','California T',parseFloat(12+19)], ];
I want to extract the unique models from the column and create the series for the line chart
function extractColumn(arr, column) {
function reduction(previousValue, currentValue) {
previousValue.push(currentValue[column]);
return previousValue;
}
return arr.reduce(reduction, []);
}
var prima = extractColumn(qry,1);
var models = [...new Set(prima)];
with extract column I get all the values of the column 1,and with the spread operator I get the models array, without duplicate.
var testa = []; // the header of the matrix
var tabella = []; // the array for the DataTable
for (i=1; i<models.length; i++) {
riga[i]=0;
}
testa.push([{label:'Montly', type: 'string'}]);
for (i=1; i<models.length; i++) {
testa.push([{label:models[i], type:'number'}]);
} // this code create the header objects
tabella.push(testa); //push the header into the tabella array
console.log(tabella[0]); //this push is ok!
var mese=qry[1][0]; // first date value
var riga = []; // array to add as row of the matrix
riga[0]=mese; // start populating the firs row
the following code should create a new row and append it to the chart table.
It checks the date of every row of the query matrix (qry): if it's a new date append the created row to the chart table and start a new row, if not it goes on creating the new row.
for (i=1; i<qry.length; i++){ // iterate qry array
if (mese!=qry[i][0]) { // check if the date is new
console.log(riga); // all the created row are ok!
tabella.push(riga); // append the row to the array
console.table(tabella); // WEIRD PROBLEM: ALL THE ARRAY ROW ARE CHANGED
mese=qry[i][0]; // EVERY CICLE!
for (x=1; x<models.length; x++) {
riga[x]=0;
}
riga[0]=mese; //reset the new row array and set the date
} else {
for (t=0;t<models.length;t++){
pos = models.indexOf(qry[i][1]);
if (pos != -1) {
riga[pos]=qry[i][2]
}
} // this code populates the new row.
}
}
The following code create the chart
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable(tabella);
var options = {
title: 'Montly Trend Focus on Complaint Type',
colors: ['#D9D904','#2A55FF','#000000'],
backgroundColor: {fill:'#FAFAFA',strokeWidth:1},
chartArea: {width:1800,height:380,left:70},
curveType: 'none',
legend: { position: 'bottom' },
hAxis: {direction:-1},
annotations: {
alwaysOutside: true,
textStyle: {
fontSize: 12,
bold: true,
color: '#000000',
}
}
};
var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
chart.draw(data, options);
}
this is an image from my console:
I solved my problem redeclareing the new row in the loop:
for (i=1; i<qry.length; i++){
if (mese!=qry[i][0]) {
tabella.push(riga);
var riga=[]; // NEW DECLARATION
mese=qry[i][0];
for (x=1; x<models.length; x++) {
riga[x]=0;
}
riga[0]=mese;
}
else {
for (t=0;t<models.length;t++){
pos = models.indexOf(qry[i][1]);
if (pos != -1){riga[pos]=qry[i][2]}
}
}
}
Hope this will be usefull for others.