Recherche…


Composant D3.js avec ReactJS

Cet exemple est basé sur un article de blog de Nicolas Hery . Il utilise les classes ES6 et les méthodes de cycle de vie de ReactJS pour maintenir le composant D3 à jour


d3_react.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Hello, d3React!</title>
  <style>
    .d3Component {
      width: 720px;
      height: 120px;
    }
  </style>
</head>
<script src="https://fb.me/react-15.2.1.min.js"></script>
<script src="https://fb.me/react-dom-15.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>

<body>
  <div id="app" />
  <script type="text/babel" src="d3_react.js"></script>
</body>

</html>

d3_react.js

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      d3React: new d3React()
    };
    this.getd3ReactState = this.getd3ReactState.bind(this);
  }

  getd3ReactState() {
    // Using props and state, calculate the d3React state
    return ({
      data: {
        x: 0,
        y: 0,
        width: 42,
        height: 17,
        fill: 'red'
      }
    });
  }

  componentDidMount() {
    var props = {
      width: this._d3Div.clientWidth,
      height: this._d3Div.clientHeight
    };
    var state = this.getd3ReactState();
    this.state.d3React.create(this._d3Div, props, state);
  }

  componentDidUpdate(prevProps, prevState) {
    var state = this.getd3ReactState();
    this.state.d3React.update(this._d3Div, state);
  }

  componentWillUnmount() {
    this.state.d3React.destroy(this._d3Div);
  }

  render() {
    return (
      <div>
        <h1>{this.props.message}</h1>
        <div className="d3Component" ref={(component) => { this._d3Div = component; } } />
      </div>
    );
  }
}

class d3React {
  constructor() {
    this.create = this.create.bind(this);
    this.update = this.update.bind(this);
    this.destroy  = this.destroy.bind(this);
    this._drawComponent = this._drawComponent.bind(this);
  }

  create(element, props, state) {
    console.log('d3React create');
    var svg = d3.select(element).append('svg')
      .attr('width', props.width)
      .attr('height', props.height);

    this.update(element, state);
  }

  update(element, state) {
    console.log('d3React update');
    this._drawComponent(element, state.data);
  }

  destroy(element) {
    console.log('d3React destroy');
  }

  _drawComponent(element, data) {
    // perform all drawing on the element here
    var svg = d3.select(element).select('svg');

    svg.append('rect')
      .attr('x', data.x)
      .attr('y', data.y)
      .attr('width', data.width)
      .attr('height', data.height)
      .attr('fill', data.fill);
  }
}

ReactDOM.render(<App message="Hello, D3.js and React!"/>, document.getElementById('app'));

Placez le contenu de d3_react.html et d3_react.js dans le même répertoire et naviguez dans un navigateur Web vers le fichier d3React.html. Si tout se passe bien, vous verrez un en-tête indiquant Hello, D3.js and React! rendu du composant React et un rectangle rouge ci-dessous du composant D3 personnalisé.

React utilise des références pour "atteindre" l'instance du composant. Les méthodes de cycle de vie de la classe d3React nécessitent cette référence pour ajouter, modifier et supprimer des éléments DOM. La classe d3React peut être étendue pour créer plus de composants personnalisés et insérée partout où un div.d3Component est créé par React.

D3js avec angulaire

L'utilisation de D3js avec Angular peut ouvrir de nouvelles perspectives, comme la mise à jour en direct des cartes dès la mise à jour des données. Nous pouvons encapsuler une fonctionnalité de graphique complète dans une directive angulaire, ce qui la rend facilement réutilisable.

index.html >>

<!DOCTYPE html>
<html ng-app="myApp">
<head>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script data-require="[email protected]" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
  <script src="app.js"></script>
  <script src="bar-chart.js"></script>
</head>

<body>

  <div ng-controller="MyCtrl">
    <!-- reusable d3js bar-chart directive, data is sent using isolated scope -->
    <bar-chart data="data"></bar-chart>
  </div>

</body>
</html>

Nous pouvons transmettre les données au graphique à l'aide du contrôleur et surveiller tout changement dans les données pour permettre la mise à jour en direct du graphique dans la directive:

app.js >>

angular.module('myApp', [])
  .controller('MyCtrl', function($scope) {
    $scope.data = [50, 40, 30];
    $scope.$watch('data', function(newVal, oldVal) {
      $scope.data = newVal;
    }, true);
  });

Enfin, la définition de la directive. Le code que nous écrivons pour créer et manipuler le graphique sera placé dans la fonction de lien de la directive.

Notez que nous avons également mis une scope. $ Watch dans la directive pour mettre à jour dès que le contrôleur passe de nouvelles données. Nous réaffectons de nouvelles données à notre variable de données s'il y a un changement de données et appelons ensuite la fonction repaintChart (), qui effectue le rendu du graphique.

bar-chart.js >>

angular.module('myApp').directive('barChart', function($window) {
  return {
    restrict: 'E',
    replace: true,
    scope: {
      data: '='
    },
    template: '<div id="bar-chart"></div>',
    link: function(scope, element, attrs, fn) {

      var data = scope.data;
      var d3 = $window.d3;
      var rawSvg = element;

      var colors = d3.scale.category10();

      var canvas = d3.select(rawSvg[0])
        .append('svg')
        .attr("width", 300)
        .attr("height", 150);

      // watching for any changes in the data
      // if new data is detected, the chart repaint code is run
      scope.$watch('data', function(newVal, oldVal) {
        data = newVal;
        repaintChart();
      }, true);

      var xscale = d3.scale.linear()
        .domain([0, 100])
        .range([0, 240]);

      var yscale = d3.scale.linear()
        .domain([0, data.length])
        .range([0, 120]);

      var bar = canvas.append('g')
        .attr("id", "bar-group")
        .attr("transform", "translate(10,20)")
        .selectAll('rect')
        .data(data)
        .enter()
        .append('rect')
        .attr("class", "bar")
        .attr("height", 15)
        .attr("x", 0)
        .attr("y", function(d, i) {
          return yscale(i);
        })
        .style("fill", function(d, i) {
          return colors(i);
        })
        .attr("width", function(d) {
          return xscale(d);
        });

      // changing the bar widths according to the changes in data
      function repaintChart() {
        canvas.selectAll('rect')
          .data(data)
          .transition()
          .duration(800)
          .attr("width", function(d) {
            return xscale(d);
          })
      }
    }
  }
});

Voici le JSFiddle qui fonctionne.

Diagramme D3.js avec Angular v1

HTML:

<div ng-app="myApp" ng-controller="Controller">
    <some-chart data="data"></some-chart>
</div>

Javascript:

angular.module('myApp', [])
.directive('someChart', function() {
    return {
        restrict: 'E',
        scope: {data: '=data'},
        link: function (scope, element, attrs) {
        var chartElement = d3.select(element[0]);
            // here you have scope.data and chartElement
            // so you may do what you want
        } 
    };
});

function Controller($scope) {
    $scope.data = [1,2,3,4,5]; // useful data
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow