Morris Chart with angular and dynamic data - angularjs

I need a area chart with using angular directive and data is fetched from database but the problem is after fetching the data ,chart is not appear and when i use static data chart appears fine.And chart would update after 10 seconds that is my requirement.
var myapp=angular.module('myapp',['ngRoute']);
myapp.controller('GraphController',
function(dataFactory,$scope,$http,$timeout){
var getData=function() {
dataFactory.httpRequest('/graph').then(function (data) {
console.log(JSON.stringify(data));
$scope.myModel=JSON.stringify(data);
$timeout(getData, 1000);
});
}
$scope.xkey = 'id';
$scope.ykeys = ['id', 'value'];
$scope.labels = ['ID', 'Value'];
$timeout(getData, 1000);
/* Static data and when these static data are use in getData function ,it didnot work.
$scope.myModel = [
{"id":21,"yr":2000,"value":80},
{"id":1,"yr":2001,"value":5},
{"id":2,"yr":2002,"value":6},
{"id":3,"yr":2003,"value":17},
{"id":4,"yr":2004,"value":5},
{"id":5,"yr":2005,"value":22},{"id":7,"yr":2006,"value":41},
{"id":9,"yr":2007,"value":11},{"id":10,"yr":2008,"value":33},
{"id":8,"yr":2009,"value":77},{"id":6,"yr":2010,"value":55},
{"id":11,"yr":2011,"value":55},{"id":12,"yr":2012,"value":66},
{"id":13,"yr":2013,"value":77},{"id":14,"yr":2014,"value":50},
{"id":15,"yr":2015,"value":22},{"id":16,"yr":2016,"value":77},
{"id":17,"yr":2017,"value":41},{"id":18,"yr":2018,"value":20},
{"id":19,"yr":2019,"value":9},{"id":20,"yr":2020,"value":2},
{"id":23,"yr":2022,"value":1}
];*/
});
myapp.directive('areachart',function(){ //directive name must be in small letters
return {
// required to make it work as an element
restrict: 'E',
template: '<div></div>',
replace: true,
link:function($scope,element,attrs)
{
var data=$scope[attrs.data],
xkey=$scope[attrs.xkey],
ykeys=$scope[attrs.ykeys],
labels=$scope[attrs.labels];
Morris.Area({
element:element,//element means id #
data:data,
xkey:xkey,
ykeys:ykeys,
labels:labels,
parseTime: false,
ymax:120,//Max. bound for Y-values
lineColors: ['#0b62a4', '#D58665'],//Array containing colors for the series lines/points.
smooth: true,//Set to false to disable line smoothing.
hideHover: 'auto',//Set to false to always show a hover legend.
pointSize: 4,//Diameter of the series points, in pixels.s
axes:true,//Set to false to disable drawing the x and y axes.
resize: true,//Set to true to enable automatic resizing when the containing element resizes
fillOpacity:1.0,//Change the opacity of the area fill colour. Accepts values between 0.0 (for completely transparent) and 1.0 (for completely opaque).
grid:true,//Set to false to disable drawing the horizontal grid lines.
});
}
}
})
Html Page
<body ng-app="myapp">
<div ng-controller="GraphController">
<AreaChart xkey="xkey" ykeys="ykeys" labels="labels" data="myModel"></AreaChart>
</div>
</body>

Angularjs MVC. Try this:
$scope.dt = angular.fromJson(sReturn.data);
Works for me.

try this $scope.myModel=JSON.parse(data); insted of $scope.myModel=JSON.stringify(data);

Related

How to add scroll-bar in the chart

Hi I have created a sample chart using public data, but it has more data to display in the chart, so I'm trying to add scroll-bar along x-axis so that all the data are displayed neatly. But I'm finding it difficult to add the scroll-bar to x-axis, so how to add scroll-bar to the chart. I have attached a plnkr for viewing. Please help me.Thank you :)
Controller:
var myApp = angular.module('app',["chart.js"]);
myApp.controller("chartController",function($scope,$http){
$scope.totalDocks = [];
$scope.availDocks =[];
$http.get("http://citibikenyc.com/stations/json")
.then(function(item){
var dataFetched = item;
console.log(dataFetched.data.stationBeanList[0]);
for(var i=0; i < 100 ; i++){
$scope.totalDocks.push(dataFetched.data.stationBeanList[i].totalDocks);
$scope.availDocks.push(dataFetched.data.stationBeanList[i].availableDocks);
}
})
})
Plunker File
Since angular.chart is responsive by itself, you do not have to add scroll bar manually, instead inorder to make chart to appear claerly you can make ticks along x axis to be skipped.
Add this to your options config,
$scope.options = {
scales: {
xAxes: [{
ticks: {
autoSkipPadding: 100
}
}]
},
responsive: true,
maintainAspectRatio: true
};
DEMO

How to make a custom legend in angular-chart.js Pie Chart

I used angular-chart.js in my website to create a Pie chart, and I would like to customize the legend. By default, the legend shows the color and the label. (As shown in the picture below) I would like to add the value/data of that label, like what it shown in the tooltip of the chart.
This is my HTML code:
<canvas id="pie" class="chart chart-pie"
chart-data="chartData" chart-labels="chartLabels" chart-options="chartOptions">
</canvas>
Based on the angular-chart.js documentation, legend is now a Chart.js option so the chart-legend attribute has been removed.
That is why, in my JS code I've tried to add generateLabels, just in case this is what I need to customize the legend:
$scope.chartOptions = {
legend: {
display: true,
labels: {
generateLabels: function(chart){
console.log(chart.config);
}
}
}
};
But whenever I add this lines, it will not show the chart. I think it is an error or something. And I'm not sure, if generateLabels is the right option that I needed.
Can somebody teach me the right way to customize the legend to achieve what I wanted?
Thanks in advance!
Let me try shedding some light/answering your question:
generateLabels: does make custom labels,and replaces templates from v1 but in order to use it you have to get your chart information and reimplement legend labels adhering to the Legend Item Interface found in the docs and code. Sounds a bit cryptic, but in practice is somehow simple and goes like this:
var theHelp = Chart.helpers;
// You need this for later
// Inside Options:
legend: {
display: true,
// generateLabels changes from chart to chart, check the source,
// this one is from the doughnut :
// https://github.com/chartjs/Chart.js/blob/master/src/controllers/controller.doughnut.js#L42
labels: {
generateLabels: function(chart) {
var data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
var meta = chart.getDatasetMeta(0);
var ds = data.datasets[0];
var arc = meta.data[i];
var custom = arc && arc.custom || {};
var getValueAtIndexOrDefault = theHelp.getValueAtIndexOrDefault;
var arcOpts = chart.options.elements.arc;
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
return {
// And finally :
text: ds.data[i] + "% of the time " + label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
index: i
};
});
}
return [];
}
}
}
Result:
Codepen: Chart.js Pie Chart Custom Legend Labels
There are other alternatives, if you notice on the pen/pie, the slices also have data information, that is from a plugin (check the pen)
Still another option, is to render the legend labels off canvas,for instance:
myPieChart.generateLegend();
Which gives you this Html:
"<ul class='0-legend'><li><span style='background-color:black'> </span>she returns it </li><li><span style='background-color:white'></span>she keeps it</li></ul>"
I haven't tried it, but I think you can modify it with the global method for your data Legend on the callback an it will give you a block of Html you can insert off canvas.

Multiple call to back-end (By curresponding backbone model) on clicking on pie-legend highcharts

I am using a highchart pie-legend graph in a backbone application.If i click on a portion of pie chart,it calls back-end for only 1 time. If I click again on a portion without getting chart redrawn,even then only 1 call to back end is there. But if I cause chart to be redrawn by clicking on a legend and then I click on a portion of pie-chart, it calls two times for back end. I can see it on console of firebug debugger. And if I again make chart to be redrawn by any change, calls to the back-end increase accordingly. How can I restrict these unwanted multiple calls on clicking pie-legend chart.
The click on legent is also captured and kept. And when I click on pie chart,it accumulates the number of clicks and send multiple calls to back-end through model.
Thanks is advance.
The code is as follows:
chart: {
backgroundColor: null,
events:{
redraw: function() {
var dataObj = this.series[0].data;
$.each(dataObj, function( index ) {
var pathObj = dataObj[index].graphic.element;
pathObj['contentValue']=dataObj[index].name;
$(pathObj).popoverClosable({
container:'div#chart-donut',
html: true,
trigger:'click',
placement: 'top',
content:'<div class="popover-dummy"></div>'
});
$(pathObj).on('click', function () {
//render the corresponding popover content
var spinner = new Spinner().spin();
$('div.popover-dummy').html(spinner.el);
var contentCategory=pathObj.point.name;
var model = Backbone.model;
if(!that.brandDetailsPopoverView || that.brandDetailsPopoverView == null){
that.brandDetailsPopoverView = new BrandDetailsPopoverView({model:model,brandName:that.selectedBrandName,topicId:that.curTopicId,brandId:that.brandId,contentCategory:contentCategory,groupId: that.selectedGroupId,el:"#discover-popover-content"});
}
that.brandDetailsPopoverView.render(contentCategory);
$('div.popover-dummy').html($('#discover-popover-content').html());
});
});
}
},
margin: [10,20,10,10]
},

jQuery UI Draggable / Sortable 4 column w/ CSS Transitions - Erratic Behavior

I am creating a simple drag and drop layout that incorporates 4 columns with the use of CSS easing effects when positioning. I have tried multiple solutions but nothing solves the problem. Eventually this will be an angularjs app.
Heres the JSfiddle (this is four columns so spread your browser to view the whole layout)
You will notice when you drag a module that sometimes it will let you snap to the grid and show the "placeholder" and some times it won't.
Sometimes the ".placeholder" which is the ".marker" in my example will randomly place the .marker div above the containing li elements causing this to happen.
Link to marker image
Is there a way to force the element to always be on the bottom element of all the li items of a parent ul?
Any help would be greatly appreciated!
var stylesheet = $('style[name=impostor_size]')[0].sheet;
var rule = stylesheet.rules ? stylesheet.rules[0].style : stylesheet.cssRules[0].style;
var setPadding = function(atHeight) {
rule.cssText = 'border-top-width: '+atHeight+'px';
};
$('.button-up').click(function(){
updateWidgetData();
});
$('.main-area ul').sortable({
connectWith: '.column',
cursor: 'move',
placeholder: 'marker',
forcePlaceholderSize: true,
opacity: 0.4,
start: function(event, ui){
setPadding(ui.item.height());
},
stop: function(event, ui){
ui.item.css({'top':'0','left':'0'}); //Opera fix
if(!$.browser.mozilla && !$.browser.safari)
updateWidgetData();
var next = ui.item.next();
next.css({'-moz-transition':'none', '-webkit-transition':'none', 'transition':'none'});
setTimeout(next.css.bind(next, {'-moz-transition':'border-top-width 0.1s ease-in', '-webkit-transition':'border-top-width 0.1s ease-in', 'transition':'border-top-width 0.1s ease-in'}));
}
}).disableSelection();
function updateWidgetData() {
var items = [];
$('.column').each(function() {
var columnId=$(this).attr('id');
$('.dragbox', this).each(function(i) {
var item = {
id: $(this).attr('id'),
order : i,
column: columnId
};
items.push(item);
});
});
var sortorder = { items: items };
//Pass sortorder variable to server using ajax to save state
$.post('updatePanels.php', 'data='+$.toJSON(sortorder), function(response) {
if(response=="success")
$("#console").html('<div class="success">Saved</div>').hide().fadeIn(1000);
setTimeout(function() {
$('#console').fadeOut(1000);
}, 2000);
});
}

How to have a checkbox in an ng-grid grouping

Is it possible to have a check box in an ng-grid grouping header?
When that grouping check box is clicked, all the checkboxes of the rows under that particular group should be selected.
The other group rows should remain un-selected.
Right now, when I have my gridOptions as follows, I see checkboxes for all rows, and in the table header. When a column header is dragged to use it for grouping, the grouped rows-header do not have checkboxes.
Can anybody help?
$scope.gridOptions = {
data: 'myData',
showGroupPanel: true,
showSelectionCheckbox: true,
jqueryUITheme: true,
enableCellSelection: true,
enableRowSelection: true,
enableCellEdit: false,
pinSelectionCheckbox: false,
selectWithCheckboxOnly: true
};
In order to make it, you have first to override the default aggregateTemplate :
$scope.gridOptions = {
data: 'myData',
showGroupPanel: true,
showSelectionCheckbox: true,
jqueryUITheme: true,
enableRowSelection: true,
aggregateTemplate: "<div ng-init=\"initAggregateGroup(row)\" ng-style=\"rowStyle(row)\" style=\"top: 0px; height: 48px; left: 0px; cursor:pointer\" class=\"ngAggregate\">" +
" <span class=\"ngAggregateText\" ng-click=\"expandGroupChilds($event, row)\">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>" +
"<input class=\"ngSelectionHeader\" type=\"checkbox\" ng-show=\"multiSelect\" ng-checked=\"row.status.isChecked\" ng-model=\"row.status.isChecked\" ng-change=\"toggleGroupSelectedChilds(row)\" /></div>"
};
Then you can add those functions in your controller:
$scope.expandAll = function($event){
$scope.isAllCollapsed = !$scope.isAllCollapsed;
_.each($scope.ngGridOptions.ngGrid.rowFactory.aggCache, function(row){
row.toggleExpand();
});
};
$scope.initAggregateGroup = function(group){
group.status = {
isChecked: false
};
};
$scope.toggleGroupSelectedChilds = function(group){
_.each(group.children, function(entry){
$scope.ngGridOptions.selectItem(entry.rowIndex, group.status.isChecked);
});
};
$scope.expandGroupChilds = function($event, group){
$event.stopPropagation();
$event.preventDefault();
group.toggleExpand();
};
I found this question while desperately seeking an answer to the same problem, and while Eric's answer pointed me in the right direction, it wasn't working as expected*.
Here's what I came to in the end, after a couple of days of playing around with the contents of the ngRow and ngAggregate objects.
My aggregateTemplate is pretty similar to Eric's, but there are a few differences. First, I've added the expand/collapse icon thing back in (span class="{{row.aggClass()}}></span>"). I've also moved the ng-click="expandGroupChildren($event, row)" to the containing div, and added ng-click="$event.stopPropagation()" to the checkbox. This means that clicking anywhere but the checkbox will expand/collapse the group. Finally, I've renamed some functions.
var aggregateTemplate = '<div ng-init="initAggregateGroup(row)" ng-style="rowStyle(row)" style="top: 0; height: 48px; left: 0; cursor:pointer" class="ngAggregate" ng-click="expandGroupChildren($event, row)">' +
'<span class="{{row.aggClass()}}"></span>' +
'<input class="ngSelectionHeader" type="checkbox" ng-show="multiSelect" ng-checked="row.status.isChecked" ng-model="row.status.isChecked" ng-change="setGroupSelection(row)" ng-click="$event.stopPropagation()" />' +
'<span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>' +
'</div>';
But the function code is where I really diverge.
First, this just doesn't work for me:
$scope.gridOptions.selectItem(entry.rowIndex, group.status.isChecked)
Instead, in this version we call this:
row.selectionProvider.setSelection (row, group.status.isChecked)
Since it takes the row itself instead of an index, it just works, no matter how tangled the indices might get.
This version also works with nested groups. When you group by, for instance, City > Age, and you get a group Minnesota containing a group 53, clicking on the group header for 53 will select all 53-year-olds who live in Minesota, but not 53-year-olds in other cities or Minnesotans of other ages. Clicking the group header for Minnesota, on the other hand, will select everyone in Minnesota, and the group header checkboxes for every sub-group. Likewise, if we only have 53-year-olds in the the Minnesota group, then clicking the 53 checkbox will also tick the Minnesota checkbox.
And that brings me to the final change. With a watcher per group (I don't generally like watchers and try to avoid them, but sometimes they're a necessary evil), we keep track of the selection within each group and automatically tick the box in the group header when every row is selected. Just like the select-all checkbox at the top of the grid.
So here's the code:
angular.extend ($scope, {
initAggregateGroup : initAggregateGroup,
expandGroupChildren : expandGroupChildren,
setGroupSelection : setGroupSelection
});
function initAggregateGroup (group) {
group.status = {
isChecked: getGroupSelection (group)
};
$scope.$watch (
function () {
return getGroupSelection (group)
},
function (isSelected) {
setGroupSelection (group, isSelected);
}
)
}
function expandGroupChildren ($event, group) {
$event.stopPropagation ();
$event.preventDefault ();
group.toggleExpand ();
}
function setGroupSelection (group, isSelected) {
// Sets the field when called by the watcher (in which case the field needs to be updated)
// or during recursion (in which case the objects inside aggChildren don't have it set at all)
if (_.isBoolean (isSelected)) {
group.status = { isChecked : isSelected }
}
// Protects against infinite digest loops caused by the watcher above
if (group.status.isChecked === getGroupSelection (group)) { return }
_.forEach (group.children, function (row) {
// children: ngRow objects that represent actual data rows
row.selectionProvider.setSelection (row, group.status.isChecked);
});
_.forEach (group.aggChildren, function (subGroup) {
// aggChildren: ngAggregate objects that represent groups
setGroupSelection (subGroup, group.status.isChecked);
});
}
function getGroupSelection (group) {
if (group.children.length > 0) {
return _.every (group.children, 'selected');
}
if (group.aggChildren.length > 0) {
return _.every (group.aggChildren, getGroupSelection);
}
return false;
}
*Clicking the checkbox in the aggregateTemplate would select a seemingly random collection of rows from all across the grid (seemingly random, because it was consistent throughout separate sessions, for the same data).
I think the problem (at least for me in ngGrid 2.0.12) was that ngGrid wasn't properly mapping the rowIndex field to the right row in its model. I think this was because the rows were rearranged for the grouping as well as the sorting, and the internal mapping hadn't kept up.

Resources