This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Create a D3 Chart as an Angular Directive

8:55 Angular 1.x lesson by

Integrating D3 with Angular can be very simple. In this lesson, you will learn basic integration as well as how to create D3 charts that can be packaged as AngularJS directives.

Get the Code Now
click to level up comment guidelines


Integrating D3 with Angular can be very simple. In this lesson, you will learn basic integration as well as how to create D3 charts that can be packaged as AngularJS directives.

In this lesson, we're going to take our existing area chart and integrate it with Angular. Right now, we've got some global click handlers, and we're just using jQuery along with D3 and lodash. You can see here, we have some nice transitions in place, but let's get this integrated.

If we go ahead and add the Angular source tags, we're going to add Angular itself, as well as ngRoute, so we can do some basic routing. We'll go ahead and add the ngApp attribute, so that we can get our app bootstrapped, and then we'll go ahead and add a ngView to hold our routed templates.

Next, we will create the actual template. We're just going to do it in line here, so if we create this template tag, we're going to give it an ID of ChartTPL, which we'll see, again in a second here. But so if we create this template tag, and then move our markup into it, we have a template that we can use from our route provider.

The last thing is, we'll just convert these onclicks to ng-clicks, so that we can call a method that's specified on our scope. We can actually go ahead and get rid of jQuery as well, since we were just using that to load data, and we're going to use Angular for that now.

If we go over here to our JavaScript, and then we go to create our app module, and give it a dependent module of ngRoute so that we get our routing in here, then we can go ahead and add our route provider and our config hook.

We've got our route provider, and we're just going to handle the route URL here. When we hit that route URL, we're going to use our ChartTPL template that we created previously, and then we're going to use a controller called app controller. The next thing to do, then, is to actually create this app controller.

Our controller, of course gets the scope injected, but we're also going to inject HTTP so that we can make our remote call and load our data. The first thing we'll do is go ahead and create a couple of skeleton functions that we are going to use. We're going to have one for creating the chart, and one for updating the chart.

Update chart is actually going to take a subject argument, remember that's the one that we're calling from these click handlers, and so that's what we'll use to actually switch between our subjects.

The last thing, we just want to expose that update chart function on the scope there, so that it can actually be called. Now that we have that, we can go ahead and use the HTTP dependency to load our data. We will call HTTP GET, and then pull this URL out that we had been loading from jQuery, and get our data that way.

Once that completes, we'll use the then hook, and call CreateChart, and from there, we'll call UpdateChart. We'll create it and then go ahead and update it to the first state. Although, since UpdateChart needs an argument, we'll just do an in-line function here, and then call UpdateChart with our initial subject of math.

Now that we have those function skeletons in, we can go ahead and grab this code, and move it into our controller. When we create the chart, we're going to go through and format our data the way we need to, and then we'll go ahead and create the SVG tag and everything. UpdateChart, we can actually just pull from down here, which I guess we could've just grabbed that whole method, but that's OK.

We'll pull this up here, paste that in and then we should be good to go, but we have an error, and that is because we were not passing in the result from our remote call. That HTTP GET call is going to pass that result in to CreateChart, and then we actually need to assign that data to our subjects array so that it can be used by the rest of our code.

Retry that, and there we go. Now we've got a working chart, and we're back to where we started. That's cool, but a more realistic use case is probably to encapsulate your charts within a directive. Let's look at how to do that. You can see that I have updated the markup here, so that our buttons are now calling SetSubject instead of UpdateChart.

The next thing that we'll do is actually update this div. We're going to convert this into our directive by adding the area chart attribute. We've got an area chart class name, and then we're also binding to chart data. Before we get into the JavaScript, let's go ahead and update our CSS so that we're not using that ID selector anymore, we're just using that area chart class.

We can then go ahead and create our directive definition here, but before we do that, let's clarify and simplify the controller's role here. What we're actually going to be doing with the controller is basically just loading the data and assigning it, and making it available for the rest of the application. Rather than calling CreateChart when the HTTP call finishes, we're just going to use our own function here.

We'll grab a little bit of this out of CreateChart so that that function will make the data available and then do that little bit of formatting that we need on that. That's really going to be all that our controller handles for the most part. We can go ahead and snip out this code, and instead of calling UpdateChart once our data is loaded and set up, we actually want to call the SetSubject method, which is what our buttons also now call from the markup.

We'll define SetSubject as a new function here, and it's still going to take in that subject, but rather than assigning data to that chart, it's just going to make it available on the scope so that that binding and our directive will be able to work. When we call SetSubject, that will update chart data appropriately, and then we'll just update this call here, this line here that exposes SetSubject so that our buttons can call it.

We can go ahead and fill in our directive. We're just going to paste in the functions that we pulled out of our controller, and define our directive definition object. You saw we had a chart data property that we are going to bind to, so we're going to define that on the scope, and then we'll set up our link function.

Our link function is going to stay small, but it's very important because this is where we are going to call CreateChart. Once we have a reference to the element that we are defined on in the UI, we're going to actually pass that element itself, and use the erase in text to get the actual element, and we're going to pass that to CreateChart.

CreateChart is receiving an actual element reference, and this is the biggest key in the whole thing, is that now our D3 code is not going to use a CSS selector to select something to build the chart inside of, it's going to use that actual element that was passed in.

We now have a fully contained D3 chart. One other thing we can do to make things just a little more portable and flexible is we can actually use the dimensions of that element to set our width and height properties. We can clear them out here, and just use that code directly. Whatever is defined in that CSS selector is actually going to determine the size of our chart.

The last thing we need to do in our actual directive inside of our link function is we will set up a watcher for that chart data property. Our controller is going to be updating that as appropriate, and so we need to watch for changes in that. Make sure that we've actually received a valid value. When we have, we'll call UpdateChart.

UpdateChart is no longer going to get a subject name. It's going to get the data itself.

We can simplify that code here, but the rest of it stays the same. Our directive is now using data that it's bound to in markup, and whenever that changes, it is going ahead and running its own update. We've got a fully self-contained D3 directive here. If we look at the markup, you can see that it is our directive tag. You can see our custom attribute there.

Just to show that this does in fact work, if we go ahead and change our CSS here, to change the size of this element in and refresh, we can see that we do in fact have a bigger chart with no changes to our JavaScript. There you go, a D3 chart that's Angular directive.

Joel's Head
Why are we asking?