2012-07-09 2 views
7

Mike ha un eccellente articolo sulla scrittura di reusable components in D3. L'articolo descrive un modello su come rendere i componenti configurabili e su come applicare il componente a una selezione.Come creare un componente riutilizzabile in D3 con un'API che manipola ciascuna istanza del componente?

Il modello consente di riutilizzare un oggetto a componente singolo con selezioni multiple unendolo ai dati; per esempio.

var chart = myChart(); 

d3.select("div.chart") 
    .data(data) 
    .call(chart); 

mia implementazione del componente è simile al seguente:

function myChart() { 

    function my(selection) { 
     selection.each(function(d, i) { 

      // generate chart here 
      // `d` is the data, `i` is the index, `this` is the element 

      var state = false; 

      var circle = d3.select(this).append("circle") 
       .attr("r", "10") 
       .style("fill", "#000") 
       .on("click", toggleState); 

      function toggleState() { 
       // this function updates the current instance trapped by this closure 
       (state = !state) 
        ? circle.style("fill", "#fff") 
        : circle.style("fill", "#000"); 
      } 
     }); 
    } 

    my.toggleState(i) { 
     // How do I access the `i`th instance of the component here? 
    } 

    return my; 
} 

Quello che vorrei raggiungere è quello di consentire al chiamante di manipolare un'istanza di questo componente dato il suo indice. Ad esempio, se il selettore div.chart restituisce una selezione con due elementi, mi piacerebbe chiamare chart.toggleState(1) e aggiornare il secondo div nella selezione.

Proprio per non confondere nessuno perché sto cercando di farlo, il chiamante deve sincronizzare due tipi di componenti insieme. Immagina di avere un componente rappresentato da un cerchio e un altro componente rappresentato da un rettangolo. Le due componenti devono essere indipendenti e non legate l'una all'altra. Devo essere in grado di creare 4 cerchi e 4 rettangoli e quando clicco su un rettangolo, vorrei poter aggiornare il cerchio corrispondente in base all'ordine dell'indice. Ho già capito come generare eventi (d3.dispatch) da un componente e fornire l'indice corrente come parametro nell'evento, ma non ho capito come chiamare in un'istanza specifica di un componente dato il suo indice.

+1

Mi sembra che il modo per farlo sarebbe selezionare solo l'elemento che è necessario aggiornare e quindi chiamare il componente. Qualsiasi altra cosa violerebbe l'incapsulamento raggiunto attraverso questo modello di componenti. –

+0

Recentemente ho finito di lavorare su un componente riutilizzabile open source, basato sull'articolo di Mikes. https://timebandit.github.io/graphSub – timebandit

risposta

0

Penso che il modo più semplice sarebbe quello di dare ad ogni componente la stessa classe. Quindi quando chiami il numero chart.toggleState(1) puoi fare d3.selectAll('.component').each(function(d, i) { if (i == index) doWhatever; });

0

I dati per una particolare selezione sono in selezione [0]. È possibile selezionare l'elemento-esima e impostare un attributo così:

var i = 3 
d3.select(mySelection[0][i]).attr("fill","#fff") 

Se si conosce l'i, si potrebbe scrivere toggleState così:

function toggleState(i) { 
    var toggle_me = d3.select(circles[0][i]) 
    var state = !(toggle_me.attr("fill") == "#fff") 
    toggle_me.attr("fill", (state ? "#fff": "#000")) 
} 

Se non si conosce la i, si potrebbe scrivere toggleState così:

function toggleState() { 
    var toggle_me = d3.select(this) 
    var i = circles[0].findIndex(function(d) { 
     return d == toggle_me[0][0] 
    }) 
    if (i !== -1) { 
     var state = !(toggle_me.attr("fill") == "#fff") 
     toggle_me.attr("fill", (state ? "#fff": "#000")) 
     // Do additional functions with i here 
     //var triange = d3.select(triangles[0][i]) 
     //... 
    } 
} 

esempio di lavoro: https://jsfiddle.net/fd7fyeoq/

Per la sa mi rispondo in un altro contesto: d3js : How to select nth element of a group?