2012-07-09 19 views
7

Mike tiene un excelente artículo sobre cómo escribir reusable components en D3. El artículo describe un patrón de cómo hacer que los componentes sean configurables y cómo aplicar el componente a una selección.Cómo crear un componente reutilizable en D3 con una API que manipule cada instancia del componente?

El patrón permite que un objeto de componente único se reutilice con múltiples selecciones uniéndolo a datos; p.ej.

var chart = myChart(); 

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

Mi implementación del componente tiene el siguiente aspecto:

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; 
} 

Lo que me gustaría lograr es permitir que la persona que llama para manipular una instancia de este componente dada su índice. Por ejemplo, si el selector div.chart anterior devuelve una selección que tiene dos elementos, me gustaría llamar al chart.toggleState(1) y hacer que actualice el 2do div en la selección.

Para no confundir a nadie por qué estoy tratando de hacer esto, la persona que llama necesita sincronizar dos tipos de componentes. Imagine que tengo un componente que está representado por un círculo y otro componente que está representado por un rectángulo. Los dos componentes deben ser independientes y no estar vinculados entre sí. Necesito poder crear 4 círculos y 4 rectángulos, y cuando hago clic en un rectángulo, me gustaría poder actualizar el círculo correspondiente según el orden de índice. Ya he descubierto cómo generar eventos (d3.dispatch) desde un componente y proporcionar el índice actual como un parámetro en el evento, pero no he descubierto cómo llamar a una instancia específica de un componente dado su índice.

+1

Me parece que la forma de hacerlo sería seleccionar solo el elemento que necesita actualizar y luego llamar al componente. Cualquier otra cosa violaría la encapsulación que logras a través de este patrón de componentes. –

+0

Recientemente terminé de trabajar en un componente reutilizable de fuente abierta, basado en el artículo de Mikes. https://timebandit.github.io/graphSub – timebandit

Respuesta

0

Creo que la manera más fácil sería dar a cada componente la misma clase. Luego, cuando llame al chart.toggleState(1), puede hacer d3.selectAll('.component').each(function(d, i) { if (i == index) doWhatever; });

0

Los datos para una selección particular están en la selección [0]. Se puede seleccionar el elemento i y establecer un atributo de esta manera:

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

Si conoce la i, se podría escribir toggleState así:

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")) 
} 

Si no conoce el i, se podría escribir toggleState así:

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]) 
     //... 
    } 
} 

ejemplo de trabajo: https://jsfiddle.net/fd7fyeoq/

Para la sa me responde en un contexto diferente ver: d3js : How to select nth element of a group?

Cuestiones relacionadas