Whilst using ng2-nvd3 combined with angular 2 routing capabilities, I've noticed that the console gets full with the following error:
d3.min.js:1 Error: <g> attribute transform: Expected number, "translate(NaN,5)"
I managed to pin point the problem to a single use-case:
This can be easily reproduced with this plunker:
How can one avoid this error?
In the ng2-nvd3 source code line 118 we can see that there is a resize handler being registered
self.chart.resizeHandler = nv.utils.windowResize(function() {...})
This resize handler needs to be cleared when the chart gets out of view (like in the case above) - yet it won't happen automatically.
To solve this one need to get a reference to the chart itself by adding
@ViewChild('chart') chart;
To the component (and naming the chart in the template by adding #chart
attribute to the nvd3 element)
This will allow us to invoke
this.chart.chart.resizeHandler.clear();
To clear out the handler from the resize event.
But we still need to know when the chart is out of view - there are many ways to achieve that, each with it's own subtleties (ngOnDestroy
/ ngOnChanges
etc.).
The one that worked for me is, though a bit aggressive, cleared the problem completely:
I've injected the root Router to the main component and subscribed to changes on it, delegating them via a service with an EventEmitter
Then I've injected that service to the chart component and registered to that event (as far as I checked, you can't just inject the router to the chart component as it won't be the root router but rather some child router and thus won't get all the route changes)
In the event handler I've created a one-off invocation of
this.chart.chart.resizeHandler.clear();
RoutingService
:
import { Injectable, EventEmitter } from 'angular2/core';
@Injectable()
export class RoutingService {
public onRouteChange$: EventEmitter<string>;
constructor() {
this.onRouteChange$ = new EventEmitter();
}
routeChange(route: string) {
this.onRouteChange$.emit(route);
}
}
MainComponent
constructor:
constructor(private router:Router, private routingService:RoutingService){
router.subscribe( val => routingService.routeChange(val));
}
ChartComponent
constructor:
constructor(private routingService:RoutingService) {
let sub:any = routingService.onRouteChange$.subscribe(route => {
if (route != 'chart') {
this.chart.chart.resizeHandler.clear();
if (sub) {
sub.unsubscribe();
}
}
});
}
You can see it in the this plunker