Search code examples
angularjsd3.jsnvd3.jsangularjs-nvd3-directives

Sometimes svg chart doesn't fit in the container but it works when screen refresh


Sometimes svg chart appear more small than normal. This problem is solved Reloading Screen.

//Values Graphic
$scope.$watch(vm.dataGraphic.watch, function () {
    var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;

    setTimeout(sleep,500); //patch to "solve" this issue

    function sleep(){
        vm.dataValues = getDataValues(data);
    }

});
function getDataValues(data) {
    vm.dataGraphic = data || dataGraphicTest;
    if (vm.dataGraphic.values.length == 0) {
        return [];
    } else {
        vm.dataKeyValues = transformForKeyValues(vm.dataGraphic.values, vm.dataGraphic.accumulated);

        vm.barValues = transformBarValues(vm.dataGraphic.values, vm.dataGraphic.limit);
        var lineValues = transformLineValues(vm.barValues, vm.dataGraphic.limit, vm.dataGraphic.accumulated, vm.dataGraphic.startMonthlyLimit);
        vm.maxY = calculateMaxY(vm.barValues, lineValues);

        return [
            {
            "key": vm.dataGraphic.labelX.name,
            "bar": true,
            "color": _graphicsColors.bar,
            "values": vm.barValues
            },
            {
            "key": _graphicsValorPorDefecto,
            "color": _graphicsColors.line,
            "values": lineValues
            }
        ];
    }
}

SVG element with the following html tags appears with wrong dimensions.

<g class="nvd3 nv-wrap nv-linePlusBar" transform="translate(35,10)">

This problem does not always happen, but when it happens is arranged refreshing the screen.

I think this patch is a bad idea and I would like to understand what is happening .

Thanks


Solution

  • I think your problem ultimately stems from your watch:

    $scope.$watch(vm.dataGraphic.watch, function () {
        var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;
    
        setTimeout(sleep,500); //patch to "solve" this issue
    
        function sleep(){
            vm.dataValues = getDataValues(data);
        }
    
    });
    

    The problem I see is that you're using setTimeout to call your sleep function a half second after the watch fires. As it is now, this will be fine if your $digest is still running in 500ms. setTimeout is a vanilla JavaScript function, so calling it won't notify angularjs that there are any changes after the $digest has ended. Angular only triggers a $digest after 'user-initiated' events like ajax, clicks, entering data, etc. (read more here). If the $digest is running, you'll get lucky and angularjs will just happen to see those changes. You'll need to inform angular of your changes using $scope.$apply:

    $scope.$watch(vm.dataGraphic.watch, function () {
        var data = vm.dataGraphic.watch ? $scope.$eval(vm.dataGraphic.watch) : vm.dataGraphic;
    
        setTimeout(sleep,500); //patch to "solve" this issue
    
        function sleep(){
            vm.dataValues = getDataValues(data);
            $scope.$apply();
        }
    
    });
    

    There are a lot of ways to use $scope.$apply, so be sure to check out the documentation. Also do a search on something like 'when to use $scope.$apply' and you'll find a lot of people for and against it. It does trigger another $digest loop, which can be costly if you have a lot of bindings or if you're at the end of the final digest loop and apply causes it to start over.

    I don't think it's actually a good idea to be updating your model using setTimeout from within a watch function. Does this not work without it? You may have some other async code that needs to be applied in the same fashion. Generally, you want $scope.$apply to be as close to the non-angular asynchronous code as possible.