user3501613 user3501613 - 1 month ago 6
Javascript Question

Dynamically update directive data

Sorry, maybe it's stupid question but i'm still learning.

I'm trying to make one simple line chart using D3.js in angularJS. everything working fine and in that i am using a custom directive to plot the chart using D3. I am adding the chart data in the controller itself by using $scope.initially it will plot but i will change the data in one button click but the chart is not updating automatically,

after reading angularjs documentation i understand by using $scope we can able to change the UI content dynamically.whether it is possible here or any other scenario is present in angularJs

I am a beginner in AngularJS

<div data-ng-app="chartApp" data-ng-controller="SalesController">
<h1>Today's Sales Chart</h1>
<div linear-chart chart-data="salesData"></div>
<button type="button" data-ng-click="Change()">Click Me!</button>
</div>


My JS code

var app = angular.module('chartApp', []);
app.controller('SalesController', ['$scope', function($scope){
$scope.salesData=[
{hour: 1,sales: 54},
{hour: 2,sales: 66},
{hour: 3,sales: 77},
{hour: 4,sales: 70},
{hour: 5,sales: 60},
{hour: 6,sales: 63},
{hour: 7,sales: 55},
{hour: 8,sales: 47},
{hour: 9,sales: 55},
{hour: 10,sales: 30}
];
$scope.Change = function () {
//here i am changing the data so i need to replot the chart
$scope.salesData=[
{hour: 1,sales: 14},
{hour: 2,sales: 16},
{hour: 3,sales: 77},
{hour: 4,sales: 10},
{hour: 5,sales: 60},
{hour: 6,sales: 63},
{hour: 7,sales: 55},
{hour: 8,sales: 47},
{hour: 9,sales: 55},
{hour: 10,sales: 30}
];
}
}]);
//creating one custom directive to plot the chart
app.directive('linearChart', function($window){
return{
restrict:'EA',
template:"<svg width='850' height='200'></svg>",
link: function(scope, elem, attrs){
var salesDataToPlot=scope[attrs.chartData];
//if u gave like this u can remove attributr chart-data
// var salesDataToPlot=scope.salesData
var padding = 20;
var pathClass="path";
var xScale, yScale, xAxisGen, yAxisGen, lineFun;

var d3 = $window.d3;
var rawSvg=elem.find('svg');
var svg = d3.select(rawSvg[0]);

function setChartParameters(){

xScale = d3.scale.linear()
.domain([salesDataToPlot[0].hour, salesDataToPlot[salesDataToPlot.length-1].hour])
.range([padding + 5, rawSvg.attr("width") - padding]);

yScale = d3.scale.linear()
.domain([0, d3.max(salesDataToPlot, function (d) {
return d.sales;
})])
.range([rawSvg.attr("height") - padding, 0]);

xAxisGen = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(salesDataToPlot.length - 1);

yAxisGen = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);

lineFun = d3.svg.line()
.x(function (d) {
return xScale(d.hour);
})
.y(function (d) {
return yScale(d.sales);
})
.interpolate("basis");
}

function drawLineChart() {

setChartParameters();

svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0,180)")
.call(xAxisGen);

svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(20,0)")
.call(yAxisGen);

svg.append("svg:path")
.attr({
d: lineFun(salesDataToPlot),
"stroke": "blue",
"stroke-width": 2,
"fill": "none",
"class": pathClass
});
}
drawLineChart();
}
};
});


Fiddle

Answer

Here you go a live example: http://jsfiddle.net/7dc3efrc/1/

scope.$watch(attrs.chartData, function(newValue) {
    if (newValue) {
        svg.selectAll("*").remove();
        drawLineChart(newValue);
    }
}, true);

Use $watch method of scope to trigger update on model change. Also, don't forget to clear previous graph if you don't need it (svg.selectAll("*").remove()).

Please, see full example as I've made some minor modifications to your salesDataToPlot variable (it is now a function parameter).