JSFiddle - React, Tailwind, and code Playground

by Yosuke Kurami

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<body>
    <div class="container">
    <div ng-controller="MainCtrl as main">
        <div class="header">
  <h3>D3 Sample</h3>
</div>
<div class="row marketing" ng-controller="MainCtrl as main">
  <div class="col-md-12">
    <table class="table">
      <thead>
        <tr><th>#</th><th>name</th><th>users</th><th>action</th></tr>
      </thead>
      <tbody>
        <tr ng-repeat="data in main.sampleData"><!-- data iteration {{{ -->
          <td>{{$index + 1}}</td>
          <td>{{data.name}}</td>
          <td> <input type="number" size="5" class="form-control input-sm" ng-model="data.users"> </td>
          <td>
            <button class="btn btn-sm btn-danger" ng-click="main.remove($index);">
              <span class="glyphicon glyphicon-minus-sign"></span>
              remove
            </button>
          </td>
        </tr><!-- }}} end iteration -->
        <tr><!-- new data {{{ -->
          <td>new</td>
          <td> <input type="text" size="10" placeholder="input name" class="form-control input-sm" ng-model="main.newData.name"> </td>
          <td> <input type="number" size="5" class="form-control input-sm" ng-model="main.newData.users"> </td>
          <td>
            <button class="btn btn-success btn-sm" ng-click="main.add()">
              <span class="glyphicon glyphicon-plus-sign"></span>
              add
            </button>
          </td>
        </tr><!--}}} new data -->
      </tbody>
    </table>
  </div>
  <div class="col-md-12">
    <div>
      <d3-bar data="main.sampleData" key="name" value-prop="users" label="name"></d3-bar>
    </div>
  </div>
</div>
      </div>
    </div>
</body>

JavaScript

angular.module('angularD3SampleApp', []);
angular.module('angularD3SampleApp').controller('MainCtrl', function () {

  var main = this;

  main.sampleData= [
    {name:'Chrome', users: 500},
    {name:'Internet Explorer', users: 200},
    {name:'Safari', users: 300}
  ];

  main.newData = {name: '', users: 0};

  main.add = function(){
    main.sampleData.push(main.newData);
    main.newData = {name: '', users: 0};
  };

  main.remove = function($index){
    main.sampleData.splice($index, 1);
  };

});

angular.module('angularD3SampleApp').factory('d3', function () {
  return d3;
});

angular.module('angularD3SampleApp').directive('d3Bar', function (d3, $parse) {
  return{
    restrict: 'EAC',
    scope: {data: '=', key: '@', valueProp: '@', label: '@'},
    link: function (scope, element) {
      var svg = d3.select(element[0]).append('svg').style('width', '100%');
      var colorScale = d3.scale.category20();

      var watched = {};

      var getId = $parse(scope.key || 'id');
      var getValue = $parse(scope.valueProp || 'value');
      var getLabel = $parse(scope.label || 'name');

      scope.$watchCollection('data', function () {
        if(!scope.data){
          return;
        }

        var dataSet = svg.selectAll('g.data-group').data(scope.data, getId);

        var createdGroup = dataSet.enter()
        .append('g').classed('data-group', true)
        .each(function(d){
          var self = d3.select(this);
          watched[getId(d)] = scope.$watch(function () {
            return getValue(d);
          }, function(v){
            self.select('rect').attr('width', v);
          });
        });
        createdGroup.append('rect')
        .attr('x', 130)
        .attr('height', 18)
        .attr('fill', function (d) {
          return colorScale(d.name);
        });
        createdGroup.append('text').text(getLabel).attr('height', 15);

        dataSet.exit().each(function(d){
          var id = getId(d);
          watched[id]();
          delete...