D3 in Angular, Angular in D3



D3 in Angular, Angular in D3

0 0


ng-d3-angularconnect


On Github timruffles / ng-d3-angularconnect

D3 in Angular, Angular in D3

@timruffles @sidekicksrc

My year

The proposition

You built what?!

I was blown away

Of course I said yes

I wanted to build cool things

Then I saw the code

A quest!

To unite mutual friend, and a new face

Big ideas

Enables cool things

Has big ideas too

Meet d3

d3 ≃ jQuery + data

jQuery-ish

  • select elements
  • affect those elements via chaining API

e.g

<svg id="meetD3" width="600" height="220">
  <circle r="50" cx="300" cy="110" fill="black"></circle>
</svg>

Nothing new so far

One callback API turns jQ into D3

D3's callback

d3.selectAll(".bars")
.style("background",
  function(data) {
    return data.risk > 0.9 
      ? "red" : "green";
  });

Data -> visual

d3.selectAll(".bars")
.style("background",
  function(data) {
    return data.risk > 0.9 
      ? "red" : "green";
  });

...and where does the data come from?

data()

d3.selectAll("h3")
  .data([
    {title: "jQuery"},
    {title: "D3"}
  ])

The data-join!

Driving the document with data

<svg id="join" width="600" height="220">
  <circle r="50" cx="250" cy="110"></circle>
  <circle r="50" cx="450" cy="110"></circle>
</svg>

Sync DOM + data

var update = d3.selectAll('circle')
  .data(data)
  .attr("r", getRadius)

var enter = update.enter()
  .append("circle")


var exit = update.exit()
  .remove()

var circleData = [
  {id: 1}, {id: 2}, {id: 3}
];

// renderCircles

.selectAll(

<el><el>

)

Add el Remove el

.data(

{}{}{}{}

)

Add data Remove data
=

.enter()

update

.exit()

Big idea

Idempotency

d3 components = functions that sync <DOM> & {data}

Same input, same output

component(elements0, data1) // elements1

component(elements1, data1) // elements1

component(elements1, data2) // elements2

component(elements2, data1) // elements1

So what?

Collaboration

var colData = [
  {id: 1}, {id: 2}, {id: 3}
];

renderCollaborator(colData);

d3 components = function over 2 data-sets

{data} + <DOM>

Idempotency = DOM is predictable

Idempotency = collaboration via f · g · h

Back to the quest

How to use D3's powers

Without...

Or losing benefits of Angular?

I brought 'best practice'

ng view concerns live in?

<directives>

App-flavoured HTML

<!-- BOOO!! -->
<div class='profile'></div>

<!-- YAAAAY! -->
<profile></profile>
`

One directive per chart

Approach lots of libraries have taken

<bar-chart>

<div ng-controller='ReportCtrl as ctrl'>
  <bar-chart
    series="ctrl.series"
    y-scale="ctrl.yScale"
    >
  </bar-chart>
</div>

Sometimes, great!

1:00 AM Start
  timeCtrl.item.time: 1:00 AM
  timeForm.time.$modelValue: 1:00 AM
<form ng-controller="TimeCtrl as timeCtrl" name="timeForm">
  <clock-input ng-model="timeCtrl.item.time" name="time">
  </clock-input>
</form>

ngModelController FTW!

No giant files!

But...

Our most ambitious plans were thwarted

Web-components selfishly horde <DOM>

Remember collaboration?

A dark night of the soul

How are newbies building cooler apps?

An unsettling realisation

An example

Editable chart

  • x/y plot
  • editable data
  • update on change

Ok, we've done that before

Chart component

Editing component

BORING!

Users don't care about components

...they don't even see them

PROGRAMMER VISION!

Where the ambitious...

...is sacrificed to optimise the average

Users don't care about 'best practice'

'Best practice' should be making things easier

...not limiting you to easy things

Did I want to be an enterprise architect?!

A new quest

No more programmer vision

Direct access

Show don't tell

But <form> + <svg>?

How will you validate?

Are you casting aside ng?

Writing HTML with D3?!

// the horror
var form = d3.select("#form-root")
.append("form")
.on("submit", handleSubmit)

form.append("input")
.attr("type", "range")
.attr("required", true)

form.append("input")
.attr("type", "number")
.attr("required", true)
// ... and on, and on

I needed ng & d3

Beat the tools into submission!

d3.angularize()

Flip the hierarchy!

On demand

editedSelection
.angularize(function(d, i) {
  return {
    controller: "ComplexFormCtrl",
    locals: {
      // provide Ctrl a way to hand back to D3
      $edited: edited,
    },
    templateUrl: "editors/complexForm.html",
    controllerAs: "ctrl",
    injector: "app",
    modules: ["app", "someFormModule"],
  }
})

Coding === enabling

Let's not find excuses

Let's remember why we got into code

Let's find reasons to say...

@timruffles