Relatively new to the world of AngularJS, enjoying it so far.However, I'm struggling with my attempt to loop through entries in my db and render a <canvas> for each one.
Say this is my data (shortened for brevity):
var paintings = [
{ "_id" : ObjectId("31c75"), "data" : "0,0,0,0" },
{ "_id" : ObjectId("deadb"), "data" : "1,3,0,255" }
];
Which is loaded into the controller by a factory:
app.factory('paintings', ['$http', function($http) {
var o = {
paintings: []
};
o.getAll = function() {
return $http.get('/paintings')
.success(function(data) {
angular.copy(data, o.paintings);
});
}
return o;
}]);
I'm wanting to loop through each entry and construct a <canvas> element, then pass that <canvas> element to another object (Grid) with data as an argument, which creates context and draws on that <canvas> based on the data. Simple, right? Unfortunately, I'm at a loss for how to do so and do not have the language with which to ask a more poignant question.I think problems exist in the fact that I am using inline-templates which aren't yet rendered.
This is generally the approach I am currently trying:
HTML:
<div ng-repeat="painting in paintings" some-directive-maybe="bindCanvas(painting._id)">
<canvas id="{{ painting._id }}" width="800" height="400"></canvas>
</div>
JS:
app.controller('PaintingCtrl', [
'$scope',
function($scope) {
$scope.bindCanvas(canvasId) {
var grid = new Grid(document.getElementById(canvasId));
// Have fun with grid
}
}
]);
Help me, StackOverflow. You're my only hope...
var paintings = [
{ "_id" : ObjectId("31c75"), "data" : "0,0,0,0" },
{ "_id" : ObjectId("deadb"), "data" : "1,3,0,255" }
];
paintings should be in an array.
app.controller('PaintingCtrl', [
'$scope',
function($scope) {
$scope.bindCanvas(canvasId) {
var grid = new Grid(document.getElementById(canvasId));
// Have fun with grid
}
//paintings should be on scope for ng-repeat to find it.
// If ng-repeat does not find paintings on scope then it will create a new empty paintings object on scope
$scope.paintings = [
{ _id : "31c75", data : "0,0,0,0" },
{ _id : "deadb", data : "1,3,0,255" }
];
}
]);
Update:
I have created 2 plunkers.
First, plunker just creates canvas elements with static width and height. Number of canvas elements created is based upon number of paintings in painting.json file.
Second, plunker goes a step further and creates canvas elements with dynamic width and height. Number of canvas elements created is based upon number of paintings in painting.json file. Here width and height are based upon data property in paintings.json file.
Hope this helps.
Following code also works for repeat chart on the same page.
<div ng-repeat="a in abc">
<canvas id="pieChart{{a}}" ng-bind = "bindCanvas(a)" ></canvas>
<div>
Add below code in JS file
$scope.abc = [1,2,3];
$scope.bindCanvas = function(i) {
var ctx = document.getElementById("pieChart"+i);
new Chart(ctx,{
type: 'pie',
data: {
labels: ["Tele-conference", "Projector", "Laptop", "Desktop", "Coffee-machine"],
datasets: [{
label: "Population (millions)",
backgroundColor: ["red", "green","blue","violet","yellow"],
data: [200,100,300,400,150]
}]
},
});
}
Related
I am using morris.js charts in my angular js app.
I converted it to directive like that:
barchart.js:
angular.module('app_name').directive('barchart', function () {
return {
// required to make it work as an element
restrict: 'AEC',
template: '<div class=chart_div></div>',
replace: true,
// observe and manipulate the DOM
link: function ($scope, element, attrs) {
var data = $scope[attrs.data],
xkey = $scope[attrs.xkey],
ykeys = $scope[attrs.ykeys],
labels = $scope[attrs.labels];
Morris.Bar({
element: element,
data: data,
xkey: xkey,
ykeys: ykeys,
labels: labels,
hideHover: true,
grid: false
});
}
};
});
then, in my page.html I use the directive like that:
<section class="graphs" ng-controller="ChartController">
<div class="graphs_box">
<div class="graphs_box_title">My Orders</div>
<div class="chart_bg">
<div barchart xkey="xkey" ykeys="ykeys" labels="labels" data="MyData"></div>
</div>
</div>
The problem is, when I am adding the data to the chart in 'ChartController'
like that:
getChartData = function () {
$scope.xkey = 'X';
$scope.ykeys = ['Y'];
$scope.labels = ['Total Tasks', 'Out of Budget Tasks'];
$scope.PlannedChart = [
{ range: 'A', total_tasks: 20 },
{ range: 'B', total_tasks: 35 },
{ range: 'C', total_tasks: 100 },
{ range: 'D', total_tasks: 50 }
];
};
It works. But when I try to add data from DB (json formatted) like that:
getChartData = function () {
ChartsService.getCharts("orders").success(function (data) {
$scope.xkey = 'X';
$scope.ykeys = 'Y';
$scope.labels = ['Total Tasks', 'Out of Budget Tasks'];
$scope.OrdersChart = data.Val_Chart;
});
};
It doesn't work.
The data is fetched from DB - OK (I saw it while debugging).
Also, I noticed when I debugged that the code first goes to barchart.js whith 'undefined' data, and only after that to the service that fetches the data.
I guess this is because of getCharts("orders") working asynchronously here. You'd need to call setData(data) on the object, that Morris.Bar() returns. See the Morris.js Documenation
I fixed it, with putting all the attributes for the directive in a single object and using this as link-function:
link: function ($scope, element, attrs) {
var params = $scope[attrs.params];
angular.extend(params, {element: element});
var graph = Morris.Line(params);
var refresh = function(new_params) {
graph.setData(new_params.data);
}
$scope.$watchCollection(attrs.params, refresh);
}
$scope.$watchCollection(attrs.params,refresh); observes the params object on the scope and you just give this object a new data-array, after the async loading finished. This updates it.
This is the directive element:
<barchart params="sentin_chart"></barchart>
And this ist the object in the controller:
$scope.sentin_chart = {};
$scope.sentin_chart.data = [];
$scope.sentin_chart.xkey = 'day';
$scope.sentin_chart.ykeys = ['sent_in'];
$scope.sentin_chart.labels = ['Sent in Stories'];
As I said you just need to put the new data array into this object to update the graph $scope.sentin_chart.data = %NEWDATAARRAY%
I hope this makes any sense for you.
i am using chart.js to generate charts in a meteor app.
Here is my code
function drawChart(){
var data = [
{
value: Policies.find({'purchased_cover.trip_type': 'Single Trip'}).count(),
color:"#F38630"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Annual Multi-Trip'}).count(),
color : "#E0E4CC"
},
{
value : Policies.find({'purchased_cover.trip_type': 'Backpacker'}).count(),
color : "#69D2E7"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Golf Annual'}).count(),
color : "green"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Golf'}).count(),
color : "red"
},
{
value :Policies.find({'purchased_cover.trip_type': 'Winter Sports Annual'}).count(),
color : "yellow"
}
]
var ctx = $("#pieChart").get(0).getContext("2d");
var myPieChart = new Chart(ctx);
new Chart(ctx).Pie(data);
}
Template.charts.rendered = function(){
drawChart();
};
i have few helpers to display the count in html templates and it works fine whenever the counts changes but the chart is not changing until i reload the page..i want the chart to be reactive to the changes in the collection.
You can use Tracker.autorun to rerun drawChart whenever reactive data sources it depends on change:
if (Meteor.isClient) {
function drawChart() {
...
}
Tracker.autorun(drawChart());
}
I'm building a simple app with AngularJS. The app make a async AJAX call to the server and the server returns an array like this:
{
paragraphs: [
{content: "content one"},
{content: "cnt two"},
{content: "random three"},
{content: "last one yeeaah"}
]
}
So I'm setting this content to the StorageService factory via my set method. Everything is fine here.
I'm using ng-repeat to render the results and JQuery UI sortable to be able to change the order of the elements. When an item is swapped my script is calling the StorageService.swap method and the element order in StorageService is updated, BUT ng-repeat isn't rerendering the change, but if I remove/add or change the content it's working. How I can force the angular to rerender the ng-repeat?
= JSFIDDLE =
http://jsfiddle.net/6Jzx4/3/
= Example =
When a swap occurs the ng-repeat should be rerendered, so the IDs are consecutive
= Code =
HTML
<div ng-controller="Test" sortable>
<div ng-repeat="item in data().paragraphs" class="box slide_content" id="{{$index}}">
{{item.content}}, ID: {{$index}}
</div>
<input type="button" ng-click="add()" value="Add">
</div>
JS
var App = angular.module("MyApp", []);
App.controller("Test", function($scope, StorageService) {
StorageService.set({
paragraphs: [
{content: "content one"},
{content: "cnt two"},
{content: "random three"},
{content: "last one yeeaah"}
]
});
$scope.data = StorageService.get;
$scope.add = StorageService.add;
});
App.directive("sortable", function(StorageService) {
return {
link: function(scope, element, attrs) {
$(element[0]).sortable({
cancel: ".disabled",
items: "> .slide_content:not(.disabled)",
start: function(e, t) {
t.item.data("start_pos", t.item.index());
},
stop: function(e, t) {
var r = t.item.data("start_pos");
if (r != t.item.index()) {
StorageService.sort($(this).sortable("toArray"));
}
}
});
}
};
});
App.factory('StorageService', function() {
var output = {};
return {
set: function(data) {
angular.copy(data, output);
return output;
},
get: function() {
return output;
},
add: function() {
output.paragraphs.push({
content: 'Content'
});
},
sort: function(order) {
var localOutput = [];
for (var j in order) {
var id = parseInt(order[j]);
localOutput.push(output.paragraphs[id]);
}
console.log('new order', localOutput);
output.paragraphs = localOutput;
return output;
}
};
});
Angular doesn't know you've changed the array. Executing your sort inside a scope.$apply() will address that.
Note that I've added a that variable since this changes meaning inside the apply.
var that = this;
scope.$apply(function() {
StorageService.sort($(that).sortable("toArray"));
}
But that fix uncovers other problems that appear to be caused by the interaction between the jQuery sortable and Angular (here's a fiddle that shows an attempt at resolving the problems but still has issues). These issues have been solved in Angular UI Sortable. So a good path forward may be to switch to this.
i'm trying to build an kpi's app and i'm using angular js
i want to create a list of charts , each list item shows different values and each list item chart has different type according to my Model.
i'm based on highcharts-ng directive.
i want to inject through highchart directive some attrs like value, title name and chart type
that when i will type the following ng-repeat it will create my list of charts due to attrs
<li ng-repeat="li in list">
<highchart config="chart" chartTitle="{{li.name}}" kpiValue="{{li.data}}">
</highchart>
</li>
you can find my code here
http://jsfiddle.net/Cp73s/62/
link to highcharts-ng :
https://github.com/pablojim/highcharts-ng
High chart takes its options from config attribute. In your case chart object.
Since you want different values for different chart. You have to create as may configuration as the number of charts that need to be created.
Here is the fiddle to show the approach
http://jsfiddle.net/cmyworld/cSek7/
Basically I created a controller to manage these complexities and did setup the initial object settings in the highChartController. I did it only for title property, but the idea is the same.
You have to create a list then add to that list each chart configuration. Use ng-repeat in the list of charts:
//See: https://github.com/pablojim/highcharts-ng
var myapp = angular.module('myapp', ["highcharts-ng"]);
myapp.controller('myctrl', function ($scope) {
//The list who will contain each chart
$scope.chartlist = [];
//Chart 1
$scope.chartConfig = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: [10, 15]
}],
}
//Chart 2
$scope.chartConfig2 = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: [10, 15, 12, 8, 7]
}],
}
$scope.chartlist.push($scope.chartConfig);
$scope.chartlist.push($scope.chartConfig2);
});
then in your html use ng-repeat on the list of charts:
<div ng-app="myapp">
<div ng-controller="myctrl">
<div ng-repeat="char in chartlist" class="row">
<highchart id="chart1" config="char" class="span10"></highchart>
</div>
</div>
if you want to use dinamic data you can use a foreach to create each chart config, in this example I create a chart foreach object in the array 'a':
$scope.chartlist = [];
var a = [[1, 2],[2,4]];
function chardata(){
for (var i = 0; i < a.length; i++) {
$scope.chartConfig = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: a[i]
}],
}
$scope.chartlist.push($scope.chartConfig);
}
}
chardata();
Trying to fetch JSON data when the div is clicked, but not able to see the output. I am using Backbone Collection to pull json data. Tried to output json data to console and also within another div. The content from json file is also listed below.
<div class = "test">Click </div>
<div class = "new_test"> </div>
JS
var myModel = Backbone.Model.extend();
var myCollection = Backbone.Collection.extend({
model: myModel,
url : "myjson.json"
})
var jobs = new myCollection();
var myView = Backbone.View.extend({
el : 'div',
events : {
'click div.test' : 'render'
},
initialize : function(){
jobs.fetch();
},
render : function(){
jobs.each(function(myModel){
var _comp = myModel.get('company');
$('div.new_test').html(_comp);
console.log(_comp)
})
}
})
Json File :
[
{
"company": "Ford",
"Type": "Automobile"
},
{
"company": "Nike",
"Type": "Sports"
}
]
You have to call the render function of your view to see the results. You cannot instantiate a collection object and expect to see results.
Code hasn't been tested.
var myView = Backbone.View.extend({
el : 'div',
events : {
'click div.test' : 'render'
},
initialize : function(){
jobs.fetch();
this.render();
},
render : function(){
jobs.each(function(myModel){
var _comp = myModel.get('company');
$('div.new_test').html(_comp);
console.log(_comp)
})
}
})
var yourView = new myView();
I have the same problem a few weeks ago.
Check path for Your JSON.
For example when You have structure of directories like this:
-js
- collection
collection.js
- json
myjson.json
Backbone collection You set like this:
url: 'js/json/event.json',
Check in Firefox, in Chrome we have a Cross-browser thinking
and check this:
jobs.fetch({
reset: true,
success: function (model, attributes) {
//check here if json is loaded
}
});