SlideShare a Scribd company logo
1 of 30
Download to read offline
2014/08/23 AngularJS勉強会#2 GDG神戸
AngularJSとD3.jsによる
インタラクティブデータビジュアライゼーション
おのうえ@_likr
自己紹介
✤ おのうえ (@_likr)!
✤ 博士課程の大学院生!
✤ 可視化や最適化を使った意思決定支援に興味あり
Webと可視化
✤ Canvas、SVG、WebGLといったAPI、

D3.js、Three.jsといったライブラリによって

Web上での可視化基盤が整えられた
http://threejs.org/http://d3js.org/
AngularJSでやってること
本セッションの内容
✤ D3.jsによるグラフィックスにAngularJSで

アプリとしての肉付けをしてみる!
✤ 実例を通じて、AngularJSアプリへの

非AngularJSライブラリの組み込み、

アプリのモジュール化方法を紹介
目次
1. D3.js超入門!
2. D3.jsとAngularJSを組み合わせる!
3. モジュール分割!
4. まとめ
1. D3.js超入門
D3.js
✤ http://d3js.org/!
✤ 可視化ツールを作るためのライブラリ!!
✤ (主に)SVGでグラフィックスを描く!
✤ DOM操作や便利なユーティリティ、

レイアウトアルゴリズムなどから構成
select, selectAll
selector(CSS3に準拠)で指定された要素を一つ(先頭)選択!
var selection = d3.select(selector);
!
selectorで指定された要素を全て選択!
var selection = d3.selectAll(selector);
<div>
<div></div>
<p class=“small”></p>
<span class=“small”></span>
<div id=“contents”></div>
</div>
d3.select(‘div’)
d3.select(‘div div’)
d3.select(‘div p’)
d3.select(‘p’)
d3.selectAll(‘div’)
d3.selectAll(‘div div’)
d3.selectAll(‘.small’)
d3.selectAll(‘#contents’)
d3.selectAll(‘p.small’)
✤ マークアップで記述するグラフィックス!
✤ 解像度に依存しない(拡大してもギザギザしない)
SVG
<svg width="500" height="500">
<rect x="100" y="100" width="100" height="100" fill="red"/>
<circle cx="350" cy="150" r="50" fill="yellow" stroke="blue"/>
<line x1="100" y1="300" x2="200" y2="400" stroke-width="5" stroke="green"/>
<text x="350" y="350" text-anchor="middle">AngularJS</text>
</svg>
attr, style, classed
<svg width="300" height="300">
<rect/>
<circle/>
</svg>
d3.select('svg>rect')
.attr('width', 200)
.attr('height', 200)
.attr('x', 50)
.attr('y', 50)
.style('fill', 'red')
.style('stroke', 'black')
.style('stroke-width', '5')
.classed('semitransparent', true);
!
d3.select('svg>circle')
.attr({
cx: 150,
cy: 150,
r: 100
})
.style({
fill: 'blue',
stroke: 'black'
});
<svg width="300" height="300">
<rect class=“semitransparent"
style="fill: red; stroke: black; stroke-width: 5;
y="50" x="50" height="200" width=“200"/>
<circle style="fill: blue; stroke: black;”
r="100" cy="150" cx=“150"/>
</svg>
✤ 属性、スタイル、クラスを設定する!
selection.attr(name, value)
selection.style(name, value)
selection.classed(name, value)
append
d3.select('svg')
.append('circle')
.attr({
cx: 150,
cy: 150,
r: 100
})
.style({
fill: 'blue',
stroke: 'red',
opcaity: 0.5
});
<svg width="300" height="300"></svg>
<svg width="300" height=“300">
<circle
style="fill: blue; stroke: red;”
r="100" cy="150" cx=“150”/>
</svg>
✤ 子要素を追加する!
selection.append(tagName)
data, datum
var data = [
{x: 50, y: 50, color: 'red'},
{x: 150, y: 150, color: 'green'},
{x: 250, y: 250, color: 'blue'}
];
!
d3.selectAll('circle')
.data(data)
.attr({
cx: function(d) {
return d.x;
},
cy: function(d) {
return d.y;
},
r: 30
})
.style('fill', function(d) {
return d.color;
});
✤ 要素にデータを結びつける!
selection.datum(value)
selection.data(values)
!
✤ 属性に結びつけられたデータを反映!
selection.attr(name, f)
<svg width="300" height="300">
<circle/>
<circle/>
<circle/>
</svg>
<svg width="300" height="300">
<circle style="fill: red;”
r="30" cy="50" cx="50"></circle>
<circle style="fill: green;”
r="30" cy="150" cx="150"></circle>
<circle style="fill: blue;”
r="30" cy="250" cx="250"></circle>
</svg>
enter, exit
var data = [
{x: 50, y: 50, color: 'red'},
{x: 150, y: 150, color: 'green'},
{x: 250, y: 250, color: 'blue'}
];
!
d3.select('svg')
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr({
cx: function(d) {
return d.x;
},
cy: function(d) {
return d.y;
},
r: 30
})
.style('fill', function(d) {
return d.color;
});
<svg width="300" height="300"></svg>
<svg width="300" height="300">
<circle style="fill: red;” r="30" cy="50" cx="50"></circle>
<circle style="fill: green;” r="30" cy="150" cx="150"></circle>
<circle style="fill: blue;” r="30" cy="250" cx="250"></circle>
</svg>
data
要素 Enter Selection
data
要素 Exit Selection
call
function draw(selection) {
selection
.append('circle')
.attr({
cx: function(d) {
return d.x;
},
cy: function(d) {
return d.y;
},
r: 30
})
.style('fill', function(d) {
return d.color;
});
}
var data = [
{x: 50, y: 50, color: 'red'},
{x: 150, y: 150, color: 'green'},
{x: 250, y: 250, color: 'blue'}
];
!
d3.select('svg')
.selectAll('circle')
.data(data)
.enter()
.call(draw);
cf. NVD3 ( http://nvd3.org )!
!
d3.select(‘svg’)
.datum(data)
.call(chart);
✤ f(selection)のショートカット!
selection.call(f)
!
✤ 描画のモジュール化に有効
本日の題材
✤ 麻雀スコア管理 (先週困った)!
✤ スコアのグラフを描く!
✤ AngularJSでスコア編集を実装!
✤ 双方向データバインドで

即座にグラフ更新!
✤ https://github.com/likr/gdgkobe20140823
d3.csv('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
}, function(data) {
d3.select('svg')
.datum(data)
.call(draw());
});
!
!
function draw() {
var verticalMargin = 30;
var axisWidth = 50;
var chartWidth = 500;
var chartHeight = 400;
var barWidth = 10;
var colorScale = d3.scale.category10();
!
return function(selection) {
selection.each(function(data) {
var svg = d3.select(this)
.attr({
width: chartWidth + axisWidth * 2,
height: chartHeight + verticalMargin * 2
});
!
svg.selectAll('g.chart')
.data([data])
.enter()
.call(initialize);
!
svg.selectAll('g.chart')
.call(drawBars)
.call(drawLines);
});
});
}
2. D3.jsとAngularJSを組み合わせる
まずはそのまま
var app = angular.module('app', []);
!
app.controller('MainController', function() {
d3.csv('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
}, function(data) {
d3.select('svg')
.datum(data)
.call(draw());
});
!
function draw() {
// …
}
});
(作法はともかく) グラフが描ける
$scopeでデータをバインドする
var app = angular.module('app', []);
!
app.controller('MainController', function($scope) {
$scope.data = [];
$scope.players = [];
!
d3.csv('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
}, function(data) {
d3.select('svg')
.datum(data)
.call(draw());
!
$scope.data = data;
$scope.players = Object.keys(data[0]);
$scope.$apply();
});
!
function draw() {
// …
}
});
<div class="form-group">
<button class="btn btn-default" ng-click="add()">Add</button>
</div>
<table class="table table-bordered">
<tr>
<th class="col-sm-2">Game</th>
<th class="col-sm-2"><input class="form-control" ng-model="players[0]"></th>
<th class="col-sm-2"><input class="form-control" ng-model="players[1]"></th>
<th class="col-sm-2"><input class="form-control" ng-model="players[2]"></th>
<th class="col-sm-2"><input class="form-control" ng-model="players[3]"></th>
<th class="col-sm-2">Error</th>
</tr>
<tr ng-repeat="game in data">
<td>{{$index + 1}}</td>
<td><input class="form-control" type="number" ng-model="game[players[0]]"></td>
<td><input class="form-control" type="number" ng-model="game[players[1]]"></td>
<td><input class="form-control" type="number" ng-model="game[players[2]]"></td>
<td><input class="form-control" type="number" ng-model="game[players[3]]"></td>
<td>{{sumGame(game)}}</td>
</tr>
<tr>
<td>Total</td>
<td>{{sumTotal(players[0])}}</td>
<td>{{sumTotal(players[1])}}</td>
<td>{{sumTotal(players[2])}}</td>
<td>{{sumTotal(players[3])}}</td>
<td></td>
</tr>
</table>
アプリの機能を整える
var app = angular.module('app', []);
!
app.controller('MainController', function($scope) {
d3.csv(/* … */);
!
$scope.add = function() {
var game = {};
$scope.players.forEach(function(player) {
game[player] = 0;
});
$scope.data.push(game);
};
!
$scope.sumGame = function(game) {
return $scope.players.reduce(function(sum, player) {
return sum + game[player];
}, 0);
};
!
$scope.sumTotal = function(player) {
return $scope.data.reduce(function(sum, game) {
return sum + game[player];
}, 0);
};
!
function draw() {
// …
}
});
データの編集がグラフに反映されない
$scope.$watchで更新を監視
app.controller('MainController', function($scope) {
$scope.data = [];
$scope.players = [];
!
d3.csv(/* … */;
!
// …
!
$scope.$watch('data', function() {
d3.select('svg')
.call(draw());
}, true);
!
function draw() {
// …
}
});
✤ Scope中の変数の変更を監視する

$scope.$watch(name, callback[, objectEquality])
✤ objectEqualityをtrueでdeep watch!
!
✤ $scope.$watchCollectionを使う方法も!
✤ AngularJSでD3.jsをラップしてみよう

(http://qiita.com/Quramy/items/b701b824c2f42c683aa7)
膨れ上がったコントローラー…
app.controller('MainController', function($scope) {
$scope.data = [];
$scope.players = [];
!
d3.csv('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
}, function(data) {
var players = Object.keys(data[0]);
!
d3.select('svg')
.datum({
keys: players,
values: data
})
.call(draw());
!
$scope.data = data;
$scope.players = players;
$scope.$apply();
});
!
$scope.add = function() {
var game = {};
$scope.players.forEach(function(player) {
game[player] = 0;
});
$scope.data.push(game);
};
!
$scope.sumGame = function(game) {
return $scope.players.reduce(function(sum, player)
{
return sum + game[player];
}, 0);
};
!
$scope.sumTotal = function(player) {
return $scope.data.reduce(function(sum, game) {
return sum + game[player];
}, 0);
};
!
$scope.$watch('data', function() {
d3.select('svg')
.call(draw());
}, true);
!
function draw() {
// …
}
});
3. モジュール分割
Provider
✤ Serviceを登録する!
module.factory(name, providerFunction)
module.service(name, constructor)
!
✤ Filterを登録する!
module.filter(name, filterFactory)
!
✤ Directiveを登録する!
module.directive(name, directiveFactory)
!
✤ value, constant, animation, …
module.factory(name, function(/* DI */) {
// serviceの初期化
!
return function() {
// serviceの本体
};
});
resolveでデータを取得する
var app = angular.module('app', ['ui.router']);
!
// d3.csvをラップ
app.factory('getCSV', function($q) {
return function(url, accessor) {
var deferred = $q.defer();
d3.csv(url, accessor, function(data) {
deferred.resolve(data);
});
return deferred.promise;
};
});
!
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('main', {
controller: 'MainController',
templateUrl: 'partials/main.html',
url: '/',
resolve: {
scoreData: function(getCSV) {
return getCSV('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
});
}
}
});
!
$urlRouterProvider.otherwise('/');
});
!
app.controller('MainController', function($scope, scoreData) {
$scope.players = Object.keys(scoreData[0]);
$scope.data = scoreData;
!
d3.select('svg')
.datum({
keys: $scope.players,
values: $scope.data
});
!
// …
})
✤ d3.csvをPromiseを返す関数としてラップ!
✤ 汎用的な関数の部品化なのでfactoryを使用!
✤ ngRoute/ui.router のresolveを利用してデータ取得を切り分け!
✤ 前回勉強会参照( http://www.slideshare.net/likr/angularjs-35612212 )
View用ロジックの切り分け
app.filter('sumGame', function() {
return function(game, keys) {
return keys.reduce(function(sum, key) {
return sum + game[key];
}, 0);
};
});
!
app.filter('sumTotal', function() {
return function(data, key) {
return data.reduce(function(sum, game) {
return sum + game[key];
}, 0);
};
});
<table class="table table-bordered">
<tr>
<th class="col-sm-2">Game</th>
<th class="col-sm-2"><input class="form-control"
<th class="col-sm-2"><input class="form-control"
<th class="col-sm-2"><input class="form-control"
<th class="col-sm-2"><input class="form-control"
<th class="col-sm-2">Error</th>
</tr>
<tr ng-repeat="game in data">
<td>{{$index + 1}}</td>
<td><input class="form-control" type="number" ng
<td><input class="form-control" type="number" ng
<td><input class="form-control" type="number" ng
<td><input class="form-control" type="number" ng
<td>{{game|sumGame:players}}</td>
</tr>
<tr>
<td>Total</td>
<td>{{data|sumTotal:players[0]}}</td>
<td>{{data|sumTotal:players[1]}}</td>
<td>{{data|sumTotal:players[2]}}</td>
<td>{{data|sumTotal:players[3]}}</td>
<td></td>
</tr>
</table>
{{value|filter:arg}}
↓
filter(value, arg)
SVGのDirective化
app.directive('chart', function() {
return {
restrict: 'AE',
scope: {
keys: '=',
values: '='
},
link: function(scope, element) {
d3.select(element[0])
.append('svg')
.datum({
keys: scope.keys,
values: scope.values
});
!
scope.$watch('values', function() {
d3.select('svg')
.call(draw());
}, true);
}
};
!
function draw() {
// …
}
});
E: <chart keys=“players” values=“data”></chart>
A: <div chart keys=“players” values=“data”></div>
✤ directiveのオプション!
✤ restrict!
✤ A: Attribute!
✤ C: Class!
✤ E: Element!
✤ scope!
✤ Viewから受け取る変数!
✤ link!
✤ DOMの構築
Before / After
app.controller('MainController', function($scope) {
$scope.data = [];
$scope.players = [];
!d3.csv('data/data.csv', function(row) {
for (var attr in row) {
row[attr] = +row[attr];
}
return row;
}, function(data) {
var players = Object.keys(data[0]);
! d3.select('svg')
.datum({
keys: players,
values: data
})
.call(draw());
! $scope.data = data;
$scope.players = players;
$scope.$apply();
});
!$scope.add = function() {
var game = {};
$scope.players.forEach(function(player) {
game[player] = 0;
});
$scope.data.push(game);
};
!$scope.sumGame = function(game) {
return $scope.players.reduce(function(sum, player) {
return sum + game[player];
}, 0);
};
!$scope.sumTotal = function(player) {
return $scope.data.reduce(function(sum, game) {
return sum + game[player];
}, 0);
};
!$scope.$watch('data', function() {
d3.select('svg')
.call(draw());
}, true);
!function draw() {
// …
}
});
app.controller('MainController', function($scope, scoreData) {
$scope.players = Object.keys(scoreData[0]);
$scope.data = scoreData;
!
$scope.add = function() {
var game = {};
$scope.players.forEach(function(player) {
game[player] = 0;
});
$scope.data.push(game);
};
});
✤ コントローラーがシンプルに!!
!
✤ 「お前のAngular.jsはもうMVCではない。と言われないためのTutorial

  ( http://qiita.com/icoxfog417/items/2ac773c33a8b34288551 )」

コントローラーではイベントハンドリングと

データバインディングに集中
4. まとめ
まとめ
✤ D3.js たのしい!
✤ 凝った表現はd3 pluginを探すことを推奨!
✤ とりあえずControllerに乗っければアプリが動く!
✤ まずは動かしてみる!!
✤ ProviderでControllerをスリムに保つ

More Related Content

What's hot

3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
3DCG(3Dコンピュータグラフィック)をWebGLで始めよう3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
AdvancedTechNight
 
Reading Self-descriptive FizzBuzz
Reading Self-descriptive FizzBuzzReading Self-descriptive FizzBuzz
Reading Self-descriptive FizzBuzz
Hiroyuki Morita
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
Naoyuki Yamada
 
D3によるデータビジュアライゼーション 2013.09.13
D3によるデータビジュアライゼーション 2013.09.13D3によるデータビジュアライゼーション 2013.09.13
D3によるデータビジュアライゼーション 2013.09.13
Minoru Chikamune
 

What's hot (19)

Ember.js Tokyo event 2014/09/22 (Japanese)
Ember.js Tokyo event  2014/09/22 (Japanese)Ember.js Tokyo event  2014/09/22 (Japanese)
Ember.js Tokyo event 2014/09/22 (Japanese)
 
CAジャーナルクラブ Dremel: Interactive Analysis of Web-Scale Datasets
CAジャーナルクラブ Dremel: Interactive Analysis of Web-Scale DatasetsCAジャーナルクラブ Dremel: Interactive Analysis of Web-Scale Datasets
CAジャーナルクラブ Dremel: Interactive Analysis of Web-Scale Datasets
 
⑱jQueryをおぼえよう!その4
⑱jQueryをおぼえよう!その4⑱jQueryをおぼえよう!その4
⑱jQueryをおぼえよう!その4
 
IaaS を活用した 情報セキュリティ演習環境の設計と実装
IaaS を活用した情報セキュリティ演習環境の設計と実装IaaS を活用した情報セキュリティ演習環境の設計と実装
IaaS を活用した 情報セキュリティ演習環境の設計と実装
 
物理エンジンを使って 3Dに息を吹き込む
物理エンジンを使って 3Dに息を吹き込む物理エンジンを使って 3Dに息を吹き込む
物理エンジンを使って 3Dに息を吹き込む
 
3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
3DCG(3Dコンピュータグラフィック)をWebGLで始めよう3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
3DCG(3Dコンピュータグラフィック)をWebGLで始めよう
 
jQuery超入門編
jQuery超入門編jQuery超入門編
jQuery超入門編
 
Leaflet初級編 - Web地図サイトを構築してみよう-
Leaflet初級編 - Web地図サイトを構築してみよう-Leaflet初級編 - Web地図サイトを構築してみよう-
Leaflet初級編 - Web地図サイトを構築してみよう-
 
swooleを試してみた
swooleを試してみたswooleを試してみた
swooleを試してみた
 
Algorithm 速いアルゴリズムを書くための基礎
Algorithm 速いアルゴリズムを書くための基礎Algorithm 速いアルゴリズムを書くための基礎
Algorithm 速いアルゴリズムを書くための基礎
 
Reading Self-descriptive FizzBuzz
Reading Self-descriptive FizzBuzzReading Self-descriptive FizzBuzz
Reading Self-descriptive FizzBuzz
 
第一回Miim勉強会
第一回Miim勉強会第一回Miim勉強会
第一回Miim勉強会
 
R入門とgoogle map +α
R入門とgoogle map +αR入門とgoogle map +α
R入門とgoogle map +α
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
EWD 3トレーニングコース#20 GlobalストレージのJavaScript用抽象化-(a)DocumentNodeオブジェクト
EWD 3トレーニングコース#20 GlobalストレージのJavaScript用抽象化-(a)DocumentNodeオブジェクトEWD 3トレーニングコース#20 GlobalストレージのJavaScript用抽象化-(a)DocumentNodeオブジェクト
EWD 3トレーニングコース#20 GlobalストレージのJavaScript用抽象化-(a)DocumentNodeオブジェクト
 
4D Tags
4D Tags4D Tags
4D Tags
 
Backbone model collection (jscafe 8)
Backbone model collection (jscafe 8)Backbone model collection (jscafe 8)
Backbone model collection (jscafe 8)
 
D3によるデータビジュアライゼーション 2013.09.13
D3によるデータビジュアライゼーション 2013.09.13D3によるデータビジュアライゼーション 2013.09.13
D3によるデータビジュアライゼーション 2013.09.13
 
ロールオーバーのいろいろなやり方
ロールオーバーのいろいろなやり方ロールオーバーのいろいろなやり方
ロールオーバーのいろいろなやり方
 

Viewers also liked

AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話
Yosuke Onoue
 
d3jsではじめるデータビジュアライゼーション入門
d3jsではじめるデータビジュアライゼーション入門d3jsではじめるデータビジュアライゼーション入門
d3jsではじめるデータビジュアライゼーション入門
Kohei Kadowaki
 
A WALK-IN INTERVIEW FOR FRESHER'S IN CCD
A WALK-IN INTERVIEW FOR FRESHER'S IN CCDA WALK-IN INTERVIEW FOR FRESHER'S IN CCD
A WALK-IN INTERVIEW FOR FRESHER'S IN CCD
Priya Seth
 
型理論 なんて自分には関係ないと思っているあなたへ
型理論 なんて自分には関係ないと思っているあなたへ型理論 なんて自分には関係ないと思っているあなたへ
型理論 なんて自分には関係ないと思っているあなたへ
Yusuke Matsushita
 

Viewers also liked (20)

AngularJSで業務システムUI部品化
AngularJSで業務システムUI部品化AngularJSで業務システムUI部品化
AngularJSで業務システムUI部品化
 
AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話
 
d3jsではじめるデータビジュアライゼーション入門
d3jsではじめるデータビジュアライゼーション入門d3jsではじめるデータビジュアライゼーション入門
d3jsではじめるデータビジュアライゼーション入門
 
SVGでつくるインタラクティブWebアプリケーション
SVGでつくるインタラクティブWebアプリケーションSVGでつくるインタラクティブWebアプリケーション
SVGでつくるインタラクティブWebアプリケーション
 
AngularJS勉強会「そもそもwebって」@ツクロア勉強会(2015.09.10)
AngularJS勉強会「そもそもwebって」@ツクロア勉強会(2015.09.10)AngularJS勉強会「そもそもwebって」@ツクロア勉強会(2015.09.10)
AngularJS勉強会「そもそもwebって」@ツクロア勉強会(2015.09.10)
 
Android Hacks - 合宿 Activity
Android Hacks - 合宿 ActivityAndroid Hacks - 合宿 Activity
Android Hacks - 合宿 Activity
 
インラインSVGをつかって地図っぽいものをつくってみる
インラインSVGをつかって地図っぽいものをつくってみるインラインSVGをつかって地図っぽいものをつくってみる
インラインSVGをつかって地図っぽいものをつくってみる
 
ワークショップ「D3.js」入門
ワークショップ「D3.js」入門ワークショップ「D3.js」入門
ワークショップ「D3.js」入門
 
D3.jsを使ったデータビジュアライズ勉強会
D3.jsを使ったデータビジュアライズ勉強会D3.jsを使ったデータビジュアライズ勉強会
D3.jsを使ったデータビジュアライズ勉強会
 
自動定理証明の紹介
自動定理証明の紹介自動定理証明の紹介
自動定理証明の紹介
 
A WALK-IN INTERVIEW FOR FRESHER'S IN CCD
A WALK-IN INTERVIEW FOR FRESHER'S IN CCDA WALK-IN INTERVIEW FOR FRESHER'S IN CCD
A WALK-IN INTERVIEW FOR FRESHER'S IN CCD
 
実践! D3.jsで可視化入門
実践! D3.jsで可視化入門実践! D3.jsで可視化入門
実践! D3.jsで可視化入門
 
型理論 なんて自分には関係ないと思っているあなたへ
型理論 なんて自分には関係ないと思っているあなたへ型理論 なんて自分には関係ないと思っているあなたへ
型理論 なんて自分には関係ないと思っているあなたへ
 
SVG と D3.js でちょっとリッチなデータ可視化
SVG と D3.js でちょっとリッチなデータ可視化SVG と D3.js でちょっとリッチなデータ可視化
SVG と D3.js でちょっとリッチなデータ可視化
 
証明プログラミング超入門
証明プログラミング超入門証明プログラミング超入門
証明プログラミング超入門
 
データ可視化勉強会
データ可視化勉強会データ可視化勉強会
データ可視化勉強会
 
Redmineを使ったヘルプデスクシステムでサポート業務を効率化
Redmineを使ったヘルプデスクシステムでサポート業務を効率化Redmineを使ったヘルプデスクシステムでサポート業務を効率化
Redmineを使ったヘルプデスクシステムでサポート業務を効率化
 
情報システム部門のタスク管理とIT全般統制 ~ Excel管理からの脱却 ~ (ITS Redmine #RxTstudy #5)
情報システム部門のタスク管理とIT全般統制 ~ Excel管理からの脱却 ~ (ITS Redmine #RxTstudy #5)情報システム部門のタスク管理とIT全般統制 ~ Excel管理からの脱却 ~ (ITS Redmine #RxTstudy #5)
情報システム部門のタスク管理とIT全般統制 ~ Excel管理からの脱却 ~ (ITS Redmine #RxTstudy #5)
 
細かすぎて伝わらないD3 ver.4の話
細かすぎて伝わらないD3 ver.4の話細かすぎて伝わらないD3 ver.4の話
細かすぎて伝わらないD3 ver.4の話
 
ワタシはSingletonがキライだ
ワタシはSingletonがキライだワタシはSingletonがキライだ
ワタシはSingletonがキライだ
 

Similar to AngularJSとD3.jsによるインタラクティブデータビジュアライゼーション

速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
Kazunari Hara
 
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
Masami Yabushita
 
20110714 j queryベーシック
20110714 j queryベーシック20110714 j queryベーシック
20110714 j queryベーシック
良太 増子
 
エンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJSエンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJS
Ayumi Goto
 

Similar to AngularJSとD3.jsによるインタラクティブデータビジュアライゼーション (20)

D3.js で LOD を Visualization
D3.js で LOD を VisualizationD3.js で LOD を Visualization
D3.js で LOD を Visualization
 
Play2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだことPlay2 scalaを2年やって学んだこと
Play2 scalaを2年やって学んだこと
 
【第2回】デザイナーがコードから読み解く、Androidアプリのデザインの幅を広げるコツとTips
【第2回】デザイナーがコードから読み解く、Androidアプリのデザインの幅を広げるコツとTips【第2回】デザイナーがコードから読み解く、Androidアプリのデザインの幅を広げるコツとTips
【第2回】デザイナーがコードから読み解く、Androidアプリのデザインの幅を広げるコツとTips
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
 
jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -jQuery Performance Tips – jQueryにおける高速化 -
jQuery Performance Tips – jQueryにおける高速化 -
 
Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~
 
Data Visualization meetup 2017
Data Visualization meetup 2017Data Visualization meetup 2017
Data Visualization meetup 2017
 
Aaなゲームをjsで
AaなゲームをjsでAaなゲームをjsで
Aaなゲームをjsで
 
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要 第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
第29回 SQL Server 勉強会 (JSSUG) - Azure Synapse Analytics 概要
 
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
バッテリー監視のためにバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーApiを使ってみた話
 
20110714 j queryベーシック
20110714 j queryベーシック20110714 j queryベーシック
20110714 j queryベーシック
 
Azure IoT Edge で Custom Vision
Azure IoT Edge で Custom VisionAzure IoT Edge で Custom Vision
Azure IoT Edge で Custom Vision
 
JavaScriptテンプレートエンジンで活かすData API
JavaScriptテンプレートエンジンで活かすData APIJavaScriptテンプレートエンジンで活かすData API
JavaScriptテンプレートエンジンで活かすData API
 
Arctic.js
Arctic.jsArctic.js
Arctic.js
 
エンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJSエンタープライズ分野での実践AngularJS
エンタープライズ分野での実践AngularJS
 
jQuery Mobileの基礎
jQuery Mobileの基礎jQuery Mobileの基礎
jQuery Mobileの基礎
 
CSS3 Design Recipe
CSS3 Design RecipeCSS3 Design Recipe
CSS3 Design Recipe
 
PL/CUDA - GPU Accelerated In-Database Analytics
PL/CUDA - GPU Accelerated In-Database AnalyticsPL/CUDA - GPU Accelerated In-Database Analytics
PL/CUDA - GPU Accelerated In-Database Analytics
 
Develop Web Application with Node.js + Express
Develop Web Application with Node.js + ExpressDevelop Web Application with Node.js + Express
Develop Web Application with Node.js + Express
 
Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.Android Lecture #04 @PRO&BSC Inc.
Android Lecture #04 @PRO&BSC Inc.
 

More from Yosuke Onoue

社会的決定とAHP
社会的決定とAHP社会的決定とAHP
社会的決定とAHP
Yosuke Onoue
 
CUDA 6の話@関西GPGPU勉強会#5
CUDA 6の話@関西GPGPU勉強会#5CUDA 6の話@関西GPGPU勉強会#5
CUDA 6の話@関西GPGPU勉強会#5
Yosuke Onoue
 
PythonistaがOCamlを実用する方法
PythonistaがOCamlを実用する方法PythonistaがOCamlを実用する方法
PythonistaがOCamlを実用する方法
Yosuke Onoue
 
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
Yosuke Onoue
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門
Yosuke Onoue
 
数理最適化とPython
数理最適化とPython数理最適化とPython
数理最適化とPython
Yosuke Onoue
 
Rsa暗号で彼女が出来るらしい
Rsa暗号で彼女が出来るらしいRsa暗号で彼女が出来るらしい
Rsa暗号で彼女が出来るらしい
Yosuke Onoue
 

More from Yosuke Onoue (17)

Angular 2のRenderer
Angular 2のRendererAngular 2のRenderer
Angular 2のRenderer
 
アニメーション(のためのパフォーマンス)の基礎知識
アニメーション(のためのパフォーマンス)の基礎知識アニメーション(のためのパフォーマンス)の基礎知識
アニメーション(のためのパフォーマンス)の基礎知識
 
GDG DevFest Kobe Firebaseハンズオン勉強会
GDG DevFest Kobe Firebaseハンズオン勉強会GDG DevFest Kobe Firebaseハンズオン勉強会
GDG DevFest Kobe Firebaseハンズオン勉強会
 
Polymerやってみた
PolymerやってみたPolymerやってみた
Polymerやってみた
 
asm.jsとWebAssemblyって実際なんなの?
asm.jsとWebAssemblyって実際なんなの?asm.jsとWebAssemblyって実際なんなの?
asm.jsとWebAssemblyって実際なんなの?
 
AngularFireで楽々バックエンド
AngularFireで楽々バックエンドAngularFireで楽々バックエンド
AngularFireで楽々バックエンド
 
社会的決定とAHP
社会的決定とAHP社会的決定とAHP
社会的決定とAHP
 
CUDA 6の話@関西GPGPU勉強会#5
CUDA 6の話@関西GPGPU勉強会#5CUDA 6の話@関西GPGPU勉強会#5
CUDA 6の話@関西GPGPU勉強会#5
 
Anaconda & NumbaPro 使ってみた
Anaconda & NumbaPro 使ってみたAnaconda & NumbaPro 使ってみた
Anaconda & NumbaPro 使ってみた
 
PythonistaがOCamlを実用する方法
PythonistaがOCamlを実用する方法PythonistaがOCamlを実用する方法
PythonistaがOCamlを実用する方法
 
What's New In Python 3.3をざっと眺める
What's New In Python 3.3をざっと眺めるWhat's New In Python 3.3をざっと眺める
What's New In Python 3.3をざっと眺める
 
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
PyOpenCLによるGPGPU入門 Tokyo.SciPy#4 編
 
PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門PyOpenCLによるGPGPU入門
PyOpenCLによるGPGPU入門
 
数理最適化とPython
数理最適化とPython数理最適化とPython
数理最適化とPython
 
201010ksmap
201010ksmap201010ksmap
201010ksmap
 
PyCUDAの紹介
PyCUDAの紹介PyCUDAの紹介
PyCUDAの紹介
 
Rsa暗号で彼女が出来るらしい
Rsa暗号で彼女が出来るらしいRsa暗号で彼女が出来るらしい
Rsa暗号で彼女が出来るらしい
 

AngularJSとD3.jsによるインタラクティブデータビジュアライゼーション

  • 2. 自己紹介 ✤ おのうえ (@_likr)! ✤ 博士課程の大学院生! ✤ 可視化や最適化を使った意思決定支援に興味あり
  • 8. D3.js ✤ http://d3js.org/! ✤ 可視化ツールを作るためのライブラリ!! ✤ (主に)SVGでグラフィックスを描く! ✤ DOM操作や便利なユーティリティ、
 レイアウトアルゴリズムなどから構成
  • 9. select, selectAll selector(CSS3に準拠)で指定された要素を一つ(先頭)選択! var selection = d3.select(selector); ! selectorで指定された要素を全て選択! var selection = d3.selectAll(selector); <div> <div></div> <p class=“small”></p> <span class=“small”></span> <div id=“contents”></div> </div> d3.select(‘div’) d3.select(‘div div’) d3.select(‘div p’) d3.select(‘p’) d3.selectAll(‘div’) d3.selectAll(‘div div’) d3.selectAll(‘.small’) d3.selectAll(‘#contents’) d3.selectAll(‘p.small’)
  • 10. ✤ マークアップで記述するグラフィックス! ✤ 解像度に依存しない(拡大してもギザギザしない) SVG <svg width="500" height="500"> <rect x="100" y="100" width="100" height="100" fill="red"/> <circle cx="350" cy="150" r="50" fill="yellow" stroke="blue"/> <line x1="100" y1="300" x2="200" y2="400" stroke-width="5" stroke="green"/> <text x="350" y="350" text-anchor="middle">AngularJS</text> </svg>
  • 11. attr, style, classed <svg width="300" height="300"> <rect/> <circle/> </svg> d3.select('svg>rect') .attr('width', 200) .attr('height', 200) .attr('x', 50) .attr('y', 50) .style('fill', 'red') .style('stroke', 'black') .style('stroke-width', '5') .classed('semitransparent', true); ! d3.select('svg>circle') .attr({ cx: 150, cy: 150, r: 100 }) .style({ fill: 'blue', stroke: 'black' }); <svg width="300" height="300"> <rect class=“semitransparent" style="fill: red; stroke: black; stroke-width: 5; y="50" x="50" height="200" width=“200"/> <circle style="fill: blue; stroke: black;” r="100" cy="150" cx=“150"/> </svg> ✤ 属性、スタイル、クラスを設定する! selection.attr(name, value) selection.style(name, value) selection.classed(name, value)
  • 12. append d3.select('svg') .append('circle') .attr({ cx: 150, cy: 150, r: 100 }) .style({ fill: 'blue', stroke: 'red', opcaity: 0.5 }); <svg width="300" height="300"></svg> <svg width="300" height=“300"> <circle style="fill: blue; stroke: red;” r="100" cy="150" cx=“150”/> </svg> ✤ 子要素を追加する! selection.append(tagName)
  • 13. data, datum var data = [ {x: 50, y: 50, color: 'red'}, {x: 150, y: 150, color: 'green'}, {x: 250, y: 250, color: 'blue'} ]; ! d3.selectAll('circle') .data(data) .attr({ cx: function(d) { return d.x; }, cy: function(d) { return d.y; }, r: 30 }) .style('fill', function(d) { return d.color; }); ✤ 要素にデータを結びつける! selection.datum(value) selection.data(values) ! ✤ 属性に結びつけられたデータを反映! selection.attr(name, f) <svg width="300" height="300"> <circle/> <circle/> <circle/> </svg> <svg width="300" height="300"> <circle style="fill: red;” r="30" cy="50" cx="50"></circle> <circle style="fill: green;” r="30" cy="150" cx="150"></circle> <circle style="fill: blue;” r="30" cy="250" cx="250"></circle> </svg>
  • 14. enter, exit var data = [ {x: 50, y: 50, color: 'red'}, {x: 150, y: 150, color: 'green'}, {x: 250, y: 250, color: 'blue'} ]; ! d3.select('svg') .selectAll('circle') .data(data) .enter() .append('circle') .attr({ cx: function(d) { return d.x; }, cy: function(d) { return d.y; }, r: 30 }) .style('fill', function(d) { return d.color; }); <svg width="300" height="300"></svg> <svg width="300" height="300"> <circle style="fill: red;” r="30" cy="50" cx="50"></circle> <circle style="fill: green;” r="30" cy="150" cx="150"></circle> <circle style="fill: blue;” r="30" cy="250" cx="250"></circle> </svg> data 要素 Enter Selection data 要素 Exit Selection
  • 15. call function draw(selection) { selection .append('circle') .attr({ cx: function(d) { return d.x; }, cy: function(d) { return d.y; }, r: 30 }) .style('fill', function(d) { return d.color; }); } var data = [ {x: 50, y: 50, color: 'red'}, {x: 150, y: 150, color: 'green'}, {x: 250, y: 250, color: 'blue'} ]; ! d3.select('svg') .selectAll('circle') .data(data) .enter() .call(draw); cf. NVD3 ( http://nvd3.org )! ! d3.select(‘svg’) .datum(data) .call(chart); ✤ f(selection)のショートカット! selection.call(f) ! ✤ 描画のモジュール化に有効
  • 16. 本日の題材 ✤ 麻雀スコア管理 (先週困った)! ✤ スコアのグラフを描く! ✤ AngularJSでスコア編集を実装! ✤ 双方向データバインドで
 即座にグラフ更新! ✤ https://github.com/likr/gdgkobe20140823 d3.csv('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }, function(data) { d3.select('svg') .datum(data) .call(draw()); }); ! ! function draw() { var verticalMargin = 30; var axisWidth = 50; var chartWidth = 500; var chartHeight = 400; var barWidth = 10; var colorScale = d3.scale.category10(); ! return function(selection) { selection.each(function(data) { var svg = d3.select(this) .attr({ width: chartWidth + axisWidth * 2, height: chartHeight + verticalMargin * 2 }); ! svg.selectAll('g.chart') .data([data]) .enter() .call(initialize); ! svg.selectAll('g.chart') .call(drawBars) .call(drawLines); }); }); }
  • 18. まずはそのまま var app = angular.module('app', []); ! app.controller('MainController', function() { d3.csv('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }, function(data) { d3.select('svg') .datum(data) .call(draw()); }); ! function draw() { // … } }); (作法はともかく) グラフが描ける
  • 19. $scopeでデータをバインドする var app = angular.module('app', []); ! app.controller('MainController', function($scope) { $scope.data = []; $scope.players = []; ! d3.csv('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }, function(data) { d3.select('svg') .datum(data) .call(draw()); ! $scope.data = data; $scope.players = Object.keys(data[0]); $scope.$apply(); }); ! function draw() { // … } }); <div class="form-group"> <button class="btn btn-default" ng-click="add()">Add</button> </div> <table class="table table-bordered"> <tr> <th class="col-sm-2">Game</th> <th class="col-sm-2"><input class="form-control" ng-model="players[0]"></th> <th class="col-sm-2"><input class="form-control" ng-model="players[1]"></th> <th class="col-sm-2"><input class="form-control" ng-model="players[2]"></th> <th class="col-sm-2"><input class="form-control" ng-model="players[3]"></th> <th class="col-sm-2">Error</th> </tr> <tr ng-repeat="game in data"> <td>{{$index + 1}}</td> <td><input class="form-control" type="number" ng-model="game[players[0]]"></td> <td><input class="form-control" type="number" ng-model="game[players[1]]"></td> <td><input class="form-control" type="number" ng-model="game[players[2]]"></td> <td><input class="form-control" type="number" ng-model="game[players[3]]"></td> <td>{{sumGame(game)}}</td> </tr> <tr> <td>Total</td> <td>{{sumTotal(players[0])}}</td> <td>{{sumTotal(players[1])}}</td> <td>{{sumTotal(players[2])}}</td> <td>{{sumTotal(players[3])}}</td> <td></td> </tr> </table>
  • 20. アプリの機能を整える var app = angular.module('app', []); ! app.controller('MainController', function($scope) { d3.csv(/* … */); ! $scope.add = function() { var game = {}; $scope.players.forEach(function(player) { game[player] = 0; }); $scope.data.push(game); }; ! $scope.sumGame = function(game) { return $scope.players.reduce(function(sum, player) { return sum + game[player]; }, 0); }; ! $scope.sumTotal = function(player) { return $scope.data.reduce(function(sum, game) { return sum + game[player]; }, 0); }; ! function draw() { // … } }); データの編集がグラフに反映されない
  • 21. $scope.$watchで更新を監視 app.controller('MainController', function($scope) { $scope.data = []; $scope.players = []; ! d3.csv(/* … */; ! // … ! $scope.$watch('data', function() { d3.select('svg') .call(draw()); }, true); ! function draw() { // … } }); ✤ Scope中の変数の変更を監視する
 $scope.$watch(name, callback[, objectEquality]) ✤ objectEqualityをtrueでdeep watch! ! ✤ $scope.$watchCollectionを使う方法も! ✤ AngularJSでD3.jsをラップしてみよう
 (http://qiita.com/Quramy/items/b701b824c2f42c683aa7)
  • 22. 膨れ上がったコントローラー… app.controller('MainController', function($scope) { $scope.data = []; $scope.players = []; ! d3.csv('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }, function(data) { var players = Object.keys(data[0]); ! d3.select('svg') .datum({ keys: players, values: data }) .call(draw()); ! $scope.data = data; $scope.players = players; $scope.$apply(); }); ! $scope.add = function() { var game = {}; $scope.players.forEach(function(player) { game[player] = 0; }); $scope.data.push(game); }; ! $scope.sumGame = function(game) { return $scope.players.reduce(function(sum, player) { return sum + game[player]; }, 0); }; ! $scope.sumTotal = function(player) { return $scope.data.reduce(function(sum, game) { return sum + game[player]; }, 0); }; ! $scope.$watch('data', function() { d3.select('svg') .call(draw()); }, true); ! function draw() { // … } });
  • 24. Provider ✤ Serviceを登録する! module.factory(name, providerFunction) module.service(name, constructor) ! ✤ Filterを登録する! module.filter(name, filterFactory) ! ✤ Directiveを登録する! module.directive(name, directiveFactory) ! ✤ value, constant, animation, … module.factory(name, function(/* DI */) { // serviceの初期化 ! return function() { // serviceの本体 }; });
  • 25. resolveでデータを取得する var app = angular.module('app', ['ui.router']); ! // d3.csvをラップ app.factory('getCSV', function($q) { return function(url, accessor) { var deferred = $q.defer(); d3.csv(url, accessor, function(data) { deferred.resolve(data); }); return deferred.promise; }; }); ! app.config(function($stateProvider, $urlRouterProvider) { $stateProvider .state('main', { controller: 'MainController', templateUrl: 'partials/main.html', url: '/', resolve: { scoreData: function(getCSV) { return getCSV('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }); } } }); ! $urlRouterProvider.otherwise('/'); }); ! app.controller('MainController', function($scope, scoreData) { $scope.players = Object.keys(scoreData[0]); $scope.data = scoreData; ! d3.select('svg') .datum({ keys: $scope.players, values: $scope.data }); ! // … }) ✤ d3.csvをPromiseを返す関数としてラップ! ✤ 汎用的な関数の部品化なのでfactoryを使用! ✤ ngRoute/ui.router のresolveを利用してデータ取得を切り分け! ✤ 前回勉強会参照( http://www.slideshare.net/likr/angularjs-35612212 )
  • 26. View用ロジックの切り分け app.filter('sumGame', function() { return function(game, keys) { return keys.reduce(function(sum, key) { return sum + game[key]; }, 0); }; }); ! app.filter('sumTotal', function() { return function(data, key) { return data.reduce(function(sum, game) { return sum + game[key]; }, 0); }; }); <table class="table table-bordered"> <tr> <th class="col-sm-2">Game</th> <th class="col-sm-2"><input class="form-control" <th class="col-sm-2"><input class="form-control" <th class="col-sm-2"><input class="form-control" <th class="col-sm-2"><input class="form-control" <th class="col-sm-2">Error</th> </tr> <tr ng-repeat="game in data"> <td>{{$index + 1}}</td> <td><input class="form-control" type="number" ng <td><input class="form-control" type="number" ng <td><input class="form-control" type="number" ng <td><input class="form-control" type="number" ng <td>{{game|sumGame:players}}</td> </tr> <tr> <td>Total</td> <td>{{data|sumTotal:players[0]}}</td> <td>{{data|sumTotal:players[1]}}</td> <td>{{data|sumTotal:players[2]}}</td> <td>{{data|sumTotal:players[3]}}</td> <td></td> </tr> </table> {{value|filter:arg}} ↓ filter(value, arg)
  • 27. SVGのDirective化 app.directive('chart', function() { return { restrict: 'AE', scope: { keys: '=', values: '=' }, link: function(scope, element) { d3.select(element[0]) .append('svg') .datum({ keys: scope.keys, values: scope.values }); ! scope.$watch('values', function() { d3.select('svg') .call(draw()); }, true); } }; ! function draw() { // … } }); E: <chart keys=“players” values=“data”></chart> A: <div chart keys=“players” values=“data”></div> ✤ directiveのオプション! ✤ restrict! ✤ A: Attribute! ✤ C: Class! ✤ E: Element! ✤ scope! ✤ Viewから受け取る変数! ✤ link! ✤ DOMの構築
  • 28. Before / After app.controller('MainController', function($scope) { $scope.data = []; $scope.players = []; !d3.csv('data/data.csv', function(row) { for (var attr in row) { row[attr] = +row[attr]; } return row; }, function(data) { var players = Object.keys(data[0]); ! d3.select('svg') .datum({ keys: players, values: data }) .call(draw()); ! $scope.data = data; $scope.players = players; $scope.$apply(); }); !$scope.add = function() { var game = {}; $scope.players.forEach(function(player) { game[player] = 0; }); $scope.data.push(game); }; !$scope.sumGame = function(game) { return $scope.players.reduce(function(sum, player) { return sum + game[player]; }, 0); }; !$scope.sumTotal = function(player) { return $scope.data.reduce(function(sum, game) { return sum + game[player]; }, 0); }; !$scope.$watch('data', function() { d3.select('svg') .call(draw()); }, true); !function draw() { // … } }); app.controller('MainController', function($scope, scoreData) { $scope.players = Object.keys(scoreData[0]); $scope.data = scoreData; ! $scope.add = function() { var game = {}; $scope.players.forEach(function(player) { game[player] = 0; }); $scope.data.push(game); }; }); ✤ コントローラーがシンプルに!! ! ✤ 「お前のAngular.jsはもうMVCではない。と言われないためのTutorial
   ( http://qiita.com/icoxfog417/items/2ac773c33a8b34288551 )」
 コントローラーではイベントハンドリングと
 データバインディングに集中
  • 30. まとめ ✤ D3.js たのしい! ✤ 凝った表現はd3 pluginを探すことを推奨! ✤ とりあえずControllerに乗っければアプリが動く! ✤ まずは動かしてみる!! ✤ ProviderでControllerをスリムに保つ