Ankit Gupta Ankit Gupta - 6 months ago 517
AngularJS Question

d3.v3.min.js:1 Error: <svg> attribute height: Expected length, "NaN" while loading a d3 chart in a modal popup

I have been trying to load a bigger size chart in modal popup onClick of smaller chart . I have made the chart responsive by resizing it's viewbox . But the issue i am now facing is that the first chart is becoming responsive and loading on the main page . But when i am clicking that graph to load a bigger chart in viewbox , i am getting an error d3.v3.min.js:1 Error: attribute height: Expected length, "NaN" .

And the strange thing is i am using the same directive & same controller for both the charts , for the first chart the viewbox width is giving a value but for other it's giving NaN .

Any help is appreciated thanks in advance .



var app = angular.module('chartApp', []);

app.controller('TimeReportCtrl', ['$scope', function($scope){

$scope.reportData={"other_time":0,"ais_time":0,"agendas_time":0,"preps_time":1,"meeting_time":7};

console.log($scope.reportData.other_time);

if(($scope.reportData.other_time== 0) && ($scope.reportData.ais_time==0) && ($scope.reportData.agendas_time==0) && ($scope.reportData.preps_time==0) && ($scope.reportData.meeting_time==0))
{

$scope.noData = false;
}
else
{

$scope.noData = true;
}

$scope.timeReportData=[];

for(var i in $scope.reportData){

$scope.timeReportData.push({labelData:i,count:$scope.reportData[i]});
}

$scope.timeReport=[];


for(var i=0;i<$scope.timeReportData.length;i++)
{ $scope.data=(($scope.timeReportData[i]).labelData);


switch ($scope.data){

case "meeting_time":
$scope.timeReport.push({label:"Meeting Time",count:$scope.timeReportData[i].count});
break;

case "ais_time":
$scope.timeReport.push({label:"ActionItem",count:$scope.timeReportData[i].count});
break;

case "agendas_time" :
$scope.timeReport.push({label:"Agendas Preparation",count:$scope.timeReportData[i].count});
break;
case "preps_time":
$scope.timeReport.push({label:"Meeting Preparation",count:$scope.timeReportData[i].count});
break;

case "other_time":
$scope.timeReport.push({label:"Other",count:$scope.timeReportData[i].count});
break;

}

}




}]);





app.directive('timeReport', function($parse, $window){
return{
restrict:'EA',

scope: {data: '=data'},
link: function(scope, element, attrs) {



scope.$watch('data', function (data) {

if (data) {
var data = scope.data;
var margin = {top: 20, right: 20, bottom: 30, left: 40};
var width = 400;
var height = 400;
var radius = Math.min(width, height) / 2;
var donutWidth = 50;
var legendRectSize = 16;
var legendSpacing = 6;

var color = d3.scale.category20();
var el=element[0];
var svg = d3.select(el)
.append('svg')
.attr('width', width+(margin.right+margin.left))
.attr('height', height+(margin.top+margin.bottom))
.call(responsivefy)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')')
;

var arc = d3.svg.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);

var pie = d3.layout.pie()
.value(function (d) {
return d.count;
})
.sort(null);

var tooltip = d3.select(el)
.append('div')
.attr('class', 'tooltip');

tooltip.append('div')
.attr('class', 'label');

tooltip.append('div')
.attr('class', 'count');

tooltip.append('div')
.attr('class', 'percent');


data.forEach(function (d) {
d.count = +d.count;
d.enabled = true; // NEW
});

var path = svg.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function (d, i) {
return color(d.data.label);
}) // UPDATED (removed semicolon)
.each(function (d) {
this._current = d;
}); // NEW

path.on('mouseover', function (d) {
var total = d3.sum(data.map(function (d) {
return (d.enabled) ? d.count : 0; // UPDATED
}));
var percent = Math.round(1000 * d.data.count / total) / 10;
tooltip.select('.label').html(d.data.label);
tooltip.select('.count').html(d.data.count);
tooltip.select('.percent').html(percent + '%');
tooltip.style('display', 'block');
});

path.on('mouseout', function () {
tooltip.style('display', 'none');
});



var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function (d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});

legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color)
.on('click', function (label) {
var rect = d3.select(this);
var enabled = true;
var totalEnabled = d3.sum(data.map(function (d) {
return (d.enabled) ? 1 : 0;
}));

if (rect.attr('class') === 'disabled') {
rect.attr('class', '');
} else {
if (totalEnabled < 2) return;
rect.attr('class', 'disabled');
enabled = false;
}

pie.value(function (d) {
if (d.label === label) d.enabled = enabled;
return (d.enabled) ? d.count : 0;
});

path = path.data(pie(data));

path.transition()
.duration(750)
.attrTween('d', function (d) {
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function (t) {
return arc(interpolate(t));
};
});
});

legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d) {
return d;
});



}

function responsivefy(svg) {
console.log("inside responsivefy");
// get container + svg aspect ratio
var container = d3.select(svg.node().parentNode),
width = parseInt(svg.style("width")),
height = parseInt(svg.style("height")),
aspect = width / height;
console.log("width of container");
console.log(width);

console.log(container.style("width"));

svg.attr("viewBox", "0 0 " + width + " " + height)
.attr("perserveAspectRatio", "xMinYMid")
.call(resize);



d3.select(window).on("resize." + container.attr("id"), resize);

// get width of container and resize svg to fit it
function resize() {

var targetWidth = parseInt(container.style("width"));
console.log(targetWidth);
svg.attr("width", targetWidth);
svg.attr("height", Math.round(targetWidth / aspect));
}
};
})
}
};
});

.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
display: none;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
width: 80px;
z-index: 10;
}
.legend {
font-size: 7px;
}
rect {
cursor: pointer;
stroke-width: 2;
}
rect.disabled {
fill: transparent !important;
}

<html>
<head>
<link rel="stylesheet" href="report.css" type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css">

</head>
<body>


<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-moment/0.10.3/angular-moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="script.js"></script>


<div ng-app="chartApp">


<div class="col-lg-6 col-sm-12" data-toggle="modal" data-target="#myModal" style="
width: 700px;
">


<div class="panel panel-default" ng-controller="TimeReportCtrl" ng-show="noData" style="
width: 50%;
height: 50%;
" >
<div class="panel-heading">Monthly User Report</div>




<div class="panel-body" >


<div time-report data="timeReport"></div>

</div>
</div>
</div>


<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">Reports</h4>
</div>

<div class="modal-footer">
<div class="panel panel-default" ng-controller="TimeReportCtrl" ng-show="noData" >
<div class="panel-heading">Monthly User Report</div>




<div class="panel-body" >


<div time-report data="timeReport"></div>

</div>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>

</div>
</div>
</body>
</html>




Answer

Try this way.

d3.select(window).on("resize", resize);

// get width of container and resize svg to fit it
function resize() {
    var containerWidth = parseInt(container.style("width"));
    var targetWidth = containerWidth ? containerWidth : parseInt(svg.style("width"));
    console.log(targetWidth);
    svg.attr("width", targetWidth);
    svg.attr("height", Math.round(targetWidth / aspect));
}

var app = angular.module('chartApp', []);

app.controller('TimeReportCtrl', ['$scope', function($scope){

    $scope.reportData={"other_time":0,"ais_time":0,"agendas_time":0,"preps_time":1,"meeting_time":7};

    console.log($scope.reportData.other_time);

    if(($scope.reportData.other_time== 0) && ($scope.reportData.ais_time==0) && ($scope.reportData.agendas_time==0) && ($scope.reportData.preps_time==0) && ($scope.reportData.meeting_time==0))
    {

        $scope.noData = false;
    }
    else
    {
       
        $scope.noData = true;
    }

    $scope.timeReportData=[];

    for(var i in $scope.reportData){

        $scope.timeReportData.push({labelData:i,count:$scope.reportData[i]});
    }

    $scope.timeReport=[];


    for(var i=0;i<$scope.timeReportData.length;i++)
    {   $scope.data=(($scope.timeReportData[i]).labelData);


        switch ($scope.data){

            case "meeting_time":
                $scope.timeReport.push({label:"Meeting Time",count:$scope.timeReportData[i].count});
                break;

            case "ais_time":
                $scope.timeReport.push({label:"ActionItem",count:$scope.timeReportData[i].count});
                break;

            case "agendas_time" :
                $scope.timeReport.push({label:"Agendas Preparation",count:$scope.timeReportData[i].count});
                break;
            case "preps_time":
                $scope.timeReport.push({label:"Meeting Preparation",count:$scope.timeReportData[i].count});
                break;

            case "other_time":
                $scope.timeReport.push({label:"Other",count:$scope.timeReportData[i].count});
                break;

        }

    }
   



}]);





app.directive('timeReport', function($parse, $window){
    return{
        restrict:'EA',

        scope: {data: '=data'},
        link: function(scope, element, attrs) {



            scope.$watch('data', function (data) {

                if (data) {
                    var data = scope.data;
                    var margin = {top: 20, right: 20, bottom: 30, left: 40};
                    var width = 400;
                    var height = 400;
                    var radius = Math.min(width, height) / 2;
                    var donutWidth = 50;
                    var legendRectSize = 16;
                    var legendSpacing = 6;

                    var color = d3.scale.category20();
                    var el=element[0];
                    var svg = d3.select(el)
                        .append('svg')
                        .attr('width', width+(margin.right+margin.left))
                        .attr('height', height+(margin.top+margin.bottom))
                        .call(responsivefy)
                        .append('g')
                        .attr('transform', 'translate(' + (width / 2) +
                            ',' + (height / 2) + ')')
                        ;

                    var arc = d3.svg.arc()
                        .innerRadius(radius - donutWidth)
                        .outerRadius(radius);

                    var pie = d3.layout.pie()
                        .value(function (d) {
                            return d.count;
                        })
                        .sort(null);

                    var tooltip = d3.select(el)
                        .append('div')
                        .attr('class', 'tooltip');

                    tooltip.append('div')
                        .attr('class', 'label');

                    tooltip.append('div')
                        .attr('class', 'count');

                    tooltip.append('div')
                        .attr('class', 'percent');


                    data.forEach(function (d) {
                        d.count = +d.count;
                        d.enabled = true;                                         // NEW
                    });

                    var path = svg.selectAll('path')
                        .data(pie(data))
                        .enter()
                        .append('path')
                        .attr('d', arc)
                        .attr('fill', function (d, i) {
                            return color(d.data.label);
                        })                                                        // UPDATED (removed semicolon)
                        .each(function (d) {
                            this._current = d;
                        });                // NEW

                    path.on('mouseover', function (d) {
                        var total = d3.sum(data.map(function (d) {
                            return (d.enabled) ? d.count : 0;                       // UPDATED
                        }));
                        var percent = Math.round(1000 * d.data.count / total) / 10;
                        tooltip.select('.label').html(d.data.label);
                        tooltip.select('.count').html(d.data.count);
                        tooltip.select('.percent').html(percent + '%');
                        tooltip.style('display', 'block');
                    });

                    path.on('mouseout', function () {
                        tooltip.style('display', 'none');
                    });

                  

                    var legend = svg.selectAll('.legend')
                        .data(color.domain())
                        .enter()
                        .append('g')
                        .attr('class', 'legend')
                        .attr('transform', function (d, i) {
                            var height = legendRectSize + legendSpacing;
                            var offset = height * color.domain().length / 2;
                            var horz = -2 * legendRectSize;
                            var vert = i * height - offset;
                            return 'translate(' + horz + ',' + vert + ')';
                        });

                    legend.append('rect')
                        .attr('width', legendRectSize)
                        .attr('height', legendRectSize)
                        .style('fill', color)
                        .style('stroke', color)
                        .on('click', function (label) {
                            var rect = d3.select(this);
                            var enabled = true;
                            var totalEnabled = d3.sum(data.map(function (d) {
                                return (d.enabled) ? 1 : 0;
                            }));

                            if (rect.attr('class') === 'disabled') {
                                rect.attr('class', '');
                            } else {
                                if (totalEnabled < 2) return;
                                rect.attr('class', 'disabled');
                                enabled = false;
                            }

                            pie.value(function (d) {
                                if (d.label === label) d.enabled = enabled;
                                return (d.enabled) ? d.count : 0;
                            });

                            path = path.data(pie(data));

                            path.transition()
                                .duration(750)
                                .attrTween('d', function (d) {
                                    var interpolate = d3.interpolate(this._current, d);
                                    this._current = interpolate(0);
                                    return function (t) {
                                        return arc(interpolate(t));
                                    };
                                });
                        });

                    legend.append('text')
                        .attr('x', legendRectSize + legendSpacing)
                        .attr('y', legendRectSize - legendSpacing)
                        .text(function (d) {
                            return d;
                        });



                }

                function responsivefy(svg) {
                    console.log("inside responsivefy");
                    // get container + svg aspect ratio
                    var container = d3.select(svg.node().parentNode),
                        width = parseInt(svg.style("width")),
                        height = parseInt(svg.style("height")),
                        aspect = width / height;
                        console.log("width of container");
                    console.log(width);

                    console.log(container.style("width"));

                    svg.attr("viewBox", "0 0 " + width + " " + height)
                        .attr("perserveAspectRatio", "xMinYMid")
                        .call(resize);



                    d3.select(window).on("resize", resize);

                    // get width of container and resize svg to fit it
                    function resize() {
                        var containerWidth = parseInt(container.style("width"));
                        var targetWidth = containerWidth?containerWidth:parseInt(svg.style("width"));
                        console.log(targetWidth);
                        svg.attr("width", targetWidth);
                        svg.attr("height", Math.round(targetWidth / aspect));
                    }
                };
            })
        }
    };
});
.tooltip {
    background: #eee;
    box-shadow: 0 0 5px #999999;
    color: #333;
    display: none;
    font-size: 12px;
    left: 130px;
    padding: 10px;
    position: absolute;
    text-align: center;
    top: 95px;
    width: 80px;
    z-index: 10;
}
.legend {
    font-size: 7px;
}
rect {
    cursor: pointer;
    stroke-width: 2;
}
rect.disabled {
    fill: transparent !important;
}
<html>
<head>
    <link rel="stylesheet" href="report.css" type="text/css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css">

</head>
<body>


<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-moment/0.10.3/angular-moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="script.js"></script>


<div ng-app="chartApp">


    <div class="col-lg-6 col-sm-12" data-toggle="modal" data-target="#myModal" style="
        width: 700px;
    ">


        <div  class="panel panel-default" ng-controller="TimeReportCtrl" ng-show="noData"  style="
        width: 50%;
        height: 50%;
    " >
            <div class="panel-heading">Monthly User Report</div>




            <div   class="panel-body"  >


                <div time-report data="timeReport"></div>

            </div>
        </div>
    </div>

    
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h4 class="modal-title" id="myModalLabel">Reports</h4>
                </div>

                <div class="modal-footer">
                    <div  class="panel panel-default" ng-controller="TimeReportCtrl" ng-show="noData"  >
                        <div class="panel-heading">Monthly User Report</div>




                        <div   class="panel-body"  >


                            <div time-report data="timeReport"></div>

                        </div>
                    </div>
                </div>
                <!-- /.modal-content -->
            </div>
            <!-- /.modal-dialog -->
        </div>

    </div>
</div>
</body>
</html>