angularjs data driven directives presentation slides
TRANSCRIPT
AngularJS Data Driven Directives
!by Jeremy Zerr
!Blog: http://www.jeremyzerr.com
LinkedIn: http://www.linkedin.com/in/jrzerr Twitter: http://www.twitter.com/jrzerr
Github: https://github.com/jrzerr Plunker: http://plnkr.co/users/jrzerr
Data Driven Directives
Using D3.js to create data visualization
Creating a Column Chart and Line Chart directive that uses D3
Making the chart interactive
Creating chart Data Models and Options objects that correspond to each chart
Transforming data from the server into chart Data Models compatible with the charts using a Mapper
Challenges
Create chart directives with a simple interface
Organize the data in a structured way
Transform domain data models into models compatible for displaying using the chart directives
Avoid code duplication when creating multiple similar charts
Keeping the D3 code separate from the directive to better adhere to Single Responsibility Principle
Code and Demo
Code is on Github at: AngularJS Data Driven Directives
Live demo on my web site: Basketball Scorebook
AngularJS Directive InterfacesThe more complicated the directive…
the more complicated the interface
Different ways to create your directive interface
Use directive scope to map all parameters individually
Use a small number of directive scope items to pass larger complex data structures
Directive scope with individual parameters
An example: AngularJS NVD3
about 50 total…
Directive scope with individual parameters
To me this looks a lot like:
function foo(var1, var2, var3, var4, var5, var6, …) { … }
Whenever I see a function with too many arguments, code instincts kick in and tell me that something is not designed properly:
the function is doing too much
has too many dependencies
typically solve by refactoring into subroutines or objects to encapsulate or replacing arguments with an object (or several)
Using a data structure
This is the method I prefer
Notice the $scope.gridOptions.data argument, it is a string.
Bit of magic… being a string is special, tells the ng-grid directive to $watch the data
I feel better about having another parameter like “doWatchData: true” that is explicit
Allows you to fine-tune your $watch and data-binding too, and even put the user in control of that through the directive options
Also, since there are a lot of arguments, I prefer putting a little formality around that options structure and make it into its own Object
Using a data structure
Here is an example of my ColumnChartOptions object
Default options already defined if you pass no options to the constructor.
Options you want to override can be passed into the constructor
Directive Inheritance?
Creating a Column Chart and Line Chart, they are very similar. So maybe you want directive inheritance?
I say not a good idea, a directive has a well-defined public API, you don’t want to pollute that with non-directive methods that you would need to allow overriding methods
Consider a more flexible alternative:
Within your directive link(), use a separate View object which you can then freely design with inheritance without worrying about the Directive API.
Directive (View) Inheritance FTW!
Let’s take a look at the ColumnChart directive
at charts/columnchart/ColumnChart.js
The chart creation heavy lifting is all in ColumnChartView, which inherits some of its functionality from ChartView.
Chart Objects
Each chart (Column Chart and Line Chart) have 4 types of objects:
columnChart (directive <column-chart>)
ColumnChartOptions (chart configuration options)
ColumnChartView (does the D3 rendering)
ColumnChartModel (data model displayed by ColumnChartView)
Why Create a ColumnChartModel?
Maintaining sanity when moving large complex data around is critical.
A ColumnChartModel is a great way to enforce a common data format that needs to be met before rendering the chart.
Then write Mappers that are responsible for transforming one domain Data Model into the desired Chart Data Model.
What does that process look like?
Fetch data from server using $http from either GameService or PlayerService, this is initiated from the Controller.
Before returning the data to the Controller, the response is transformed by the Services into the domain Models: GameModels and PlayerGameModels.
When controller data changes (seen by $watch), the domain Models are mapped into the appropriate Chart Data Model.
The Chart Directive is $watching the Chart Data Model, when the data changes, the Chart View is (re-)rendered.
Doing backend-less development with a $httpBackend mock
Also a part of this github project is an example of doing backend-less development by using a $httpBackend mock
Great for isolating frontend from backend for testing and also development to avoid needing a dev node server to provide a REST API.
I have taken the code from this github project and broke it out into a Plunker that adds more detail and more examples specifically on this topic.
Structuring a D3 Directive
D3 Basics
Uses SVG + HTML5 + CSS3
Has its own implementations of a lot of jQuery DOM-related functions. (selectors, styling, attributes, etc.)
It’s pretty low-level library, there’s no LineChart function. You build it up from scratch by binding data to SVG entities.
You can also integrate HTML with it too, for chart controls like tooltips, legends, buttons, etc.
D3 Chart Components
Scales for X and Y
Maps data (domain) to a scale (chart pixels)
Axis for X and Y
Displays a scale
Bind data to an SVG entity like a line or a rect
Uses the scales to map the data to a chart location/dimension and plot
Add title, legend, tooltips, buttons, etc
Add events like on clicking a bar, hovering over a data point, dragging a timeline, or transitions between different chart types or changing data sources
D3 Directive
The Directive object link function calls the render() function on the View object (ColumnChartView or LineChartView).
Take a look at the base ChartView object to find the render() function.
Calls several render functions for different visual components.
SVG, XAxis, YAxis, Title, Data
Has functions for creating each of the components: scales, axes, SVG, title, data.
These are overriden if necessary in the ColumnChartView or LineChartView objects.
D3 Directive - Closer look at ColumnChartView
Let’s take a closer look at ColumnChartView
Pieces being overriden
X and Y scales (not too much different than Line)
Data
Compare data portions
Comparing renderData()
ColumnChartView LineChartView
Want to learn more?
Look how nvd3 builds their D3 charts: nvd3
Chart.js is another alternative to D3: Chart.js
Both have Angular adapters: Angular nvd3 and Angular Chart.js
The Angular nvd3 library is used way more than Angular Chart.js
My Demo code is on my blog: AngularJS DDD Demo Page
Slides are on my blog: AngularJS DDD Presentation page
See some code from this presentation, and more, at My Plunker Page
Thanks!!
by Jeremy Zerr !
Blog: http://www.jeremyzerr.com LinkedIn: http://www.linkedin.com/in/jrzerr
Twitter: http://www.twitter.com/jrzerr Github: https://github.com/jrzerr
Plunker: http://plnkr.co/users/jrzerr