I have built out an Angular directive to display a D3 visualization. I make use of $filter
within the tickFormat
function on my y-axis like so:
ySalesAxis = d3.svg.axis()
.orient('left')
.ticks(6)
.scale(ySalesScale)
.tickFormat(function(d) {
return $filter('formatSalesValue')(d.value, 'USD');
});
The problem I'm seeing is that none of these tick labels appear when the page first loads. Indeed if I console.log($filter('formatSalesValue')(d.value, 'USD'))
I get 6 undefined
(since my ticks
property is set to 6). However, as soon as I take an action, clicking within the brush filter for example, the tick labels appear properly formatted.
My formatSalesValue
filter calls a service (async operation) because there are dozens of currencies cycling into and out of the system, details of which I retrieve from a DB. I am sure this is the reason my tick labels are undefined. What can I do to make sure these values appear right after page load? Note: I've attempted wrapping my tickFormat
function in a call to scope.$apply
but I get a digest already in progress
error.
I have built out an Angular directive to display a D3 visualization. I make use of $filter
within the tickFormat
function on my y-axis like so:
ySalesAxis = d3.svg.axis()
.orient('left')
.ticks(6)
.scale(ySalesScale)
.tickFormat(function(d) {
return $filter('formatSalesValue')(d.value, 'USD');
});
The problem I'm seeing is that none of these tick labels appear when the page first loads. Indeed if I console.log($filter('formatSalesValue')(d.value, 'USD'))
I get 6 undefined
(since my ticks
property is set to 6). However, as soon as I take an action, clicking within the brush filter for example, the tick labels appear properly formatted.
My formatSalesValue
filter calls a service (async operation) because there are dozens of currencies cycling into and out of the system, details of which I retrieve from a DB. I am sure this is the reason my tick labels are undefined. What can I do to make sure these values appear right after page load? Note: I've attempted wrapping my tickFormat
function in a call to scope.$apply
but I get a digest already in progress
error.
- 1 Might be an issue with tickFormat being called outside of a digest cycle. – lintmouse Commented Feb 29, 2016 at 21:08
- 2 If so, add scope.$apply() before your return statement - presuming you are in a directive and the linking function – JanS Commented Feb 29, 2016 at 21:09
- 1 Create demo that replicates problem – charlietfl Commented Feb 29, 2016 at 21:26
- See Replacing d3.js with pure SVG + AngularJS. – georgeawg Commented Feb 29, 2016 at 21:45
- 1 Any way you could put this into a codepen or something? I just tried calling $filter from .tween() and everything seemed to work ok. Is the value d.value in the format that formatSalesValue is expecting? – cDecker32 Commented Mar 4, 2016 at 21:08
2 Answers
Reset to default 8 +50Axis's tickFormat method runs passed callback and uses value returned by it in sync way. That's why you get undefined on the first call, as your $filter is async.
If this async call is only for performance reasons you should make it sync and look for improvements elsewhere. Should angular watch for so many independent changes it could clog with $digest cycles.
If you make JSBin I probably could tell you more.
TL;DR
Try using the Angular $timeout function.
Based on your question, the issue might be any number of things, but this is what I think is the problem:
Possible issue
It think that part of the problem is that the what you're getting is happening in a weird bit of the event loop.
$timeout is useful functions because it runs when the current stack is cleared and after n milliseconds have passed, if no time to delay is given, $timeout simply runs as soon as the stack unwinds. It's a useful trick for executing asynchronous code in a single thread.
Using $timeout with 0 delay doesn't actually mean the call back will fire-off after zero milliseconds. I just gets executed after a the queue of tasks is processed. Which is when you want to update your labels.
$timeout will actually triggers a digest when it's ready to do so.
Possible solution
$timeout(function() {
// Update the label with what es back from the server
}, 0, false);
This is a good video explaining how the Javascript event loop works: What the heck is the event loop anyway?