user3769145
user3769145

Reputation: 116

Directive not rendering on page after state change but is after direct load

I have a custom directive that is taking in data, generating an SVG image (a graph) via nvD3.js with it then outputting a generated SVG to the page.

My problem is if load the state directly then all is well and the image shows up fine but if I transition to the state (eg main menu to graph page) then the directive does not put the graph on the page.

I ran into this problem before but the fix I used then is not working now: Directive in Angular not rendering on $state.go but is on initial load

The directive setup:

return {
    restrict: "E",
    template: '<svg></svg>',
    link: function (scope, elem, attrs) {
        attrs.$observe('chartData', function (chartData) {
            var data = JSON.parse(chartData);
            if (data.graph_type === 'line') {
                lineChart(data);
            } else if (data.graph_type === 'pnb') {
                pnbChart(data);
            }
        })
    }
}

The function lineChart sets up the data and runs it through nvD3 to generate the chart. As follows with the bulk of styling taken out.

function lineChart(chartData) {
    d3.select('svg > *').remove();
    nv.addGraph(function () {
        var chart = nv.models.lineChart()
            .margin({left: 40})
            .useInteractiveGuideline(true)
            .showYAxis(true)
            .showXAxis(true)
            .showLegend(false)
            .pointSize(5)
            .pointShape('diamond')
            .duration(100)
            .margin({left: 50})
            .interpolate("linear");

        ... a bunch of styling code

        if (reference.length) {
            d3.select('svg')
                .datum([
                    {
                        values: line_data,
                        key: 'Latest Data',
                        color: '#006f66',
                        size: '5'
                    },
                    {
                        values: reference,
                        key: 'Target',
                        color: '#fec232'
                    }
                ]).call(chart);
        } else {
            d3.select('svg')
                .datum([
                    {
                        values: line_data,
                        key: 'Latest Data',
                        color: '#006f66'
                    }
                ]).call(chart);
        }

        nv.utils.windowResize(chart.update());
    });
}

I call it on the page thusly, where gdata is an object that contains the raw data to be put into the chart:

<nvd3-chart chart-data="gdata" class="svgContainer"></nvd3-chart>

There are some controls on the page that can dynamically update gdata. Doing so does change the data that is sent to the directive and it does update if the image is already shown. If it is not shown then the value of gdata does change but the image is not shown still.

The transition to the page is done through ui-sref however testing it with a plain href and the url of the page showing the graph yields no chart being displayed. Only if the page is loaded directly via the address bar does it work. Transitioning away from the image page once it has loaded properly then going back to it and it will continue to work.

This feels very much like I am missing something fundamental about variable bindings and timing of said binding.

any insights to this would be very appreciated.

Upvotes: 0

Views: 1207

Answers (2)

DasDany
DasDany

Reputation: 185

It's not the solution for your question, but it could help other people. I got the same problem as you, @user3769145.

angular.module('myApp', [])
    .directive('spinner', function ($rootScope) {
        // Directive Link
        function link(scope, element, attrs, controller, transcludeFn) {
            if (!$rootScope.spinnerCount || $rootScope.spinnerCount < 0) {
                $rootScope.spinnerCount = 0;
            }
        }

        // Directive Return
        return {
            restrict: 'E', // use only as element
            transclude: true,
            template:   '<div class="spinner mini" style="display: none;">' +
            '<img src="images/logos.svg" alt="Loading data">' +
            '<span>Loading Data...</span>' +
            '</div>',
            link: link
        };
    })

My fault was to use transclude and not using ngTransclude in the template.

Upvotes: 0

Matt Way
Matt Way

Reputation: 33141

My default answer to these sorts of rendering issue questions is usually try wrapping your rendering code in a $timeout.

eg:

$timeout(function(){
    lineChart(data);
});

This ensures the DOM has been rendered before executing any related rendering code.

Upvotes: 1

Related Questions