Existe-t-il une fonction / un objet de base dans D3 qui permettrait une journalisation "en ligne"?

Je lis le livre astucieux de Scott Murry D3 et je veux le faire:

  d3.select("body").selectAll("div")
    .log("body")
    .data(dataset)
    .log("data")
    .enter()
    .log("enter")
    .append("div")
    .log("div")
    .attr("class", "bar")
    .log("class");

Je fais actuellement cette chose horrible:

const log = function (msg, val) {
  const data = val ? val : this
  const label = msg ? msg : "data:"
  console.log(label, data)
  return data
}

Object.prototype.log = log

Cela fonctionne très bien, produisant cette sortie de console cliquable: entrez la description de l'image ici Mais .. Quelle est la méthode D3 pour ce faire?

4
backspaces 12 avril 2018 à 19:56

3 réponses

Meilleure réponse

Vous pouvez utiliser votre solution en modifiant le prototype de sélection d3 (par opposition au prototype d'objet, je ne pense pas que cela puisse être considéré comme "horrible" - même si je suis un peu partial pour jouer avec d3 en interne), mais je vous sens recherchent selection.call().

Si vous souhaitez accéder à la sélection actuelle, mais pas interrompre le chaînage de méthode, selection.call est probablement votre meilleur choix:

Appelle la fonction spécifiée une seule fois, en passant cette sélection avec tous les arguments facultatifs. Renvoie cette sélection. (docs)

Le premier paramètre de la fonction appelée sera la sélection, d'autres paramètres facultatifs peuvent être passés dans la méthode d'appel elle-même. La forme générale est:

selection.call(func, arg, arg, ...)

function func(selection, arg, arg, ...) {}

L'utilisation de selection.call pour la journalisation ne devrait pas être trop difficile à configurer avec cette méthode. Voici une maquette rapide (v4 / 5):

var dataset = d3.range(10);

  d3.select("body").selectAll("div")
    .call(log,"body")
    .data(dataset)
    .call(log,"dataset")
    .enter()
    .call(log,"enter")
    .append("div")
    .call(log,"div")
    .attr("class", "bar")
    .call(log,"bar");

function log(sel,msg) {
  console.log(msg,sel);
}

Et les résultats:

enter image description here

Voici la maquette en action.

3
Andrew Reid 12 avril 2018 à 20:17

Pour enregistrer des données dans votre chaîne, vous pouvez simplement utiliser un faux attribut:

d3.select("body").selectAll("div")
  .data(dataset)
  .enter()
  .append("div")
  .attr("fake", d=> console.log(d));

Pour les autres choses, je ne pense pas qu'il existe une méthode "d3" qui permette de se connecter et malheureusement, je pense que vous devez le faire de manière normale:

console.log(d3.select("body").selectAll("div").data(dataset).enter());
3
Pierre Capo 12 avril 2018 à 17:07

La première chose qui m'est venue à l'esprit a été l'approche proposée par Andrew Reid dans sa réponse en utilisant selection.call() . Bien que cette solution soit bonne, je pense qu'elle peut être améliorée en étendant directement la sélection de D3. Les documents de d3.selection() expliquent clairement que cette fonction peut être utilisée pour étendre le prototype de sélection.

Vous pouvez créer votre propre enregistreur de la manière suivante:

const logger = {
  log() {
    console.log(...arguments);
    return this;
  }
}

Cela peut être facilement mélangé dans le d3.selection.prototype:

Object.assign(d3.selection.prototype, logger);

La méthode simple logger.log() enregistre simplement tous les arguments passés à l'appel à la console et facilite le chaînage des méthodes en renvoyant this, qui fait référence à la sélection elle-même.

Bien sûr, on peut facilement imaginer de nombreuses méthodes de journalisation plus utiles pour imprimer des attributs, des sélections, des données et bien plus encore. La démo suivante développe le concept de base:

const logger = {
  log() {          // Generic method logging all arguments.
    console.log(...arguments);
    return this;
  },
  
  logMsg(msg) {    // Logging just a simple msg.
    console.log(msg);
    return this;
  },
  
  logSel() {       // Log the selection.
    console.log(this);
    return this;
  },
  
  logAttr(name) {  // Log the attributes with "name" for all selected elements.
    this.each(function(d, i) {
      let attr = d3.select(this).attr(name);
      console.log(`Node ${i}: ${name}=${attr}`);
    });
    return this;
  },
  
  logData() {      // Log the data bound to this selection.
    console.log(this.data());
    return this;
  },

  logNodeData() {  // Log datum per node.
    this.each(function(d, i) {
      console.log(`Node ${i}: ${d}`);
    });
    return this;
  }
};

Object.assign(d3.selection.prototype, logger);

d3.select("body")
    .logMsg("Start")
  .append("svg").selectAll(null)
    .log(1, {}, "Test")
    // .logSel()   // this doesn't work well in Stack snippets
  .data([1,2])
  .enter().append("circle")
    .attr("r", "10")
    .logAttr("r")
    .logData()
    .logNodeData()
    .logMsg("End");
<script src="https://d3js.org/d3.v5.js"></script>
3
altocumulus 13 avril 2018 à 00:11