如何使用D3导航嵌套的JSON对象?

问题描述 投票:1回答:1

在我的REACT应用程序中,我正在阅读JSON响应(使用axis),我将使用react-faux-dom传递给组件,尝试重新创建Mike Bostock的Multi-Series Line Chart(使用D3 v4)。

在我的组件中运行console.log(apiData)显示对象正如我所期望的那样 - 但是,我已经尝试了我能想到的一切,我能得到的最好的是空X和Y轴(以及我试图使用的“scratchpad”<g>元素了解我正在使用的内容。

有人可以指导我做我做错了吗?


我传入的JSON对象:

{
    "roles": [
    {
        "AA": [
        {
        "date": "20150101",
        "total": 6.0
        },
        {
        "date": "20150201",
        "total": 14.5
        },
        {
        "date": "20150301",
        "total": 14.5
        }],
        "AB": [
        {
        "date": "20150301",
        "total": 1.6
        },
        {
        "date": "20150401",
        "total": 1.6
        },
        {
        "date": "20150501",
        "total": 7.24
        }]
    }]
}

我的主要App.js文件调用组件:

import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
import { MyD3ReactComponentExport } from './D3Timeseries'


class App extends Component {

    /* ... assign API response to "apiResponse" */

    render() {
        const {
            apiResponse
        } = this.state

        return (
            <div className="App">
                <button onClick={this.requestData}>Refresh Data</button>
                <MyD3ReactComponentExport apiData={apiResponse} />
            </div>
        );
    }
}

我的react-faux-dom D3组件

import React from 'react'
import * as d3 from 'd3'
import PropTypes from 'prop-types'
import { withFauxDOM } from 'react-faux-dom'

class MyD3ReactComponent extends React.Component {

    constructor (props) {
        super(props)
        this.renderD3 = this.renderD3.bind(this)
        this.updateD3 = this.updateD3.bind(this)
    }

    componentDidMount () {
        this.renderD3()
    }

    componentDidUpdate (prevProps, prevState) {
        if (this.props.data !== prevProps.data) {
            this.updateD3()
        }
    }


    render () {
        return (
            <div>
                <h2>Amazing timeseries:</h2>
                {this.props.myChart}
            </div>
        )
    }



    renderD3() {
        const {
           apiData
        } = this.props

        let faux = this.props.connectFauxDOM('div', 'myChart');

        let svg = d3.select(faux).append('svg')
            .attr("width",960)
            .attr("height",500);

        let margin = {top: 20, right: 80, bottom: 30, left: 50},
            width = svg.attr("width") - margin.left - margin.right,
            height = svg.attr("height") - margin.top - margin.bottom,
            g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        let parseTime = d3.timeParse("%Y%m%d");

        let x = d3.scaleTime().range([0, width]),
            y = d3.scaleLinear().range([height, 0]),
            z = d3.scaleOrdinal(d3.schemeCategory10);

        let line = d3.line()
            .curve(d3.curveBasis)
            .x(function(d) { return x(d.date); })
            .y(function(d) { return y(d.total); });





        // ----- PASS apiData RESPONSE OBJECT INTO HERE ------
        if (apiData) {
            console.log( apiData )

            x.domain(d3.extent( d3.map(apiData.roles, function(d) { return  parseTime(d.date)  }) ));

            y.domain([
                d3.min( apiData.roles, function(c) { return d3.min(c, function(d) { return d.total; }); }),
                d3.max( apiData.roles, function(c) { return d3.max(c, function(d) { return d.total; }); }),
            ]);

            z.domain( Object.keys(apiData) );

            g.append("text").attr("y",height/3).attr("x", width/3).text( ">>>" + apiData.roles.map( function(d) { d }).map(function (e) { return e } ).map(function (f) { return f } ) + "<<<" ) ;

        } else {
            g.append("text").attr("y",height/2).attr("x", width/2).text( "No API DATA!!!" );
        }


        // ----Everything below here is 'stock' Bostock D3 code

        g.append("g")
            .attr("class", "axis axis--x")
            .attr("transform", "translate(0,"+ height +")")
            .call(d3.axisBottom(x));

        g.append("g")
            .attr("class", "axis axis--y")
            .call(d3.axisLeft(y))
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", "0.71em")
            .attr("fill", "#000")
            .text("Total resource");

        let city = g.selectAll(".city")
            .data(apiData)
            .enter().append("g")
            .attr("class", "city");

        city.append("path")
            .attr("class", "line")
            .attr("d", function(d) { return line(d.values); })
            .style("stroke", function(d) { return z(d.id); });

        city.append("text")
            .datum(function(d) { return {id: d.key, value: d.values[d.values.length - 1]}; })
            .attr("transform", function(r) { return function(d) { return "translate(" + x(d.date) + "," + y(d.total) + ")"; } })
            .attr("x", 3)
            .attr("dy", "0.35em")
            .style("font", "10px sans-serif")
            .text(function(d) { return d.id; });


        // function type(d, _, columns) {
        //   d.date = parseTime(d.date);
        //   for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
        //   return d;
        // }
    }


    updateD3() {
        this.props.animateFauxDOM(800)
        d3.select('text').text(this.props.title)
    }
}

MyD3ReactComponent.defaultProps = {
  myChart: 'loading'
};

MyD3ReactComponent.propTypes = {
  // title: PropTypes.string.isRequired,
  apiData: PropTypes.object.isRequired
}

const MyD3ReactComponentExport = withFauxDOM(MyD3ReactComponent);
export { MyD3ReactComponentExport }
javascript json reactjs d3.js
1个回答
0
投票

设置域的问题是,roles数组包含一个对象,您正在尝试使用map,找到d3.min并找到导致无效值的d3.max,因为这些函数仅适用于ARRAYS。

例如,

y.domain([
     d3.min( apiData.roles, function(c) { return d3.min(c, function(d) { return d.total; }); }),
     d3.max( apiData.roles, function(c) { return d3.max(c, function(d) { return d.total; }); }),
]);

在上面的场景中,使用apiData变量cminmax无法应用的对象。

建议:尽可能多地使用console.log并使用Chrome控制台或任何调试器添加断点。

为此,我首先按以下方式格式化数据:

// format data
var data = [];
apiData.roles.forEach(function(d) {
  var keys = Object.keys(d);
  keys.forEach(function(k) {
    data.push({key: k, values: d[k]});
  })
});

这是新格式化数据的外观:

[
{
"key": "AA",
"values": [
  {
    "date": "20150101",
    "total": 6
  },
  {
    "date": "20150201",
    "total": 14.5
  },
  {
    "date": "20150301",
    "total": 14.5
  }
]
},
{
"key": "AB",
"values": [
  {
    "date": "20150301",
    "total": 1.6
  },
  {
    "date": "20150401",
    "total": 1.6
  },
  {
    "date": "20150501",
    "total": 7.24
  }
]
}
]

使用此数据,可以按如下方式创建图表:(我已注释掉文本部分:请使用格式化数据来修复)

var apiData = {
    "roles": [
    {
        "AA": [
          {
          "date": "20150101",
          "total": 6.0
          },
          {
          "date": "20150201",
          "total": 14.5
          },
          {
          "date": "20150301",
          "total": 14.5
          }
        ],
        "AB": [
        {
        "date": "20150301",
        "total": 1.6
        },
        {
        "date": "20150401",
        "total": 1.6
        },
        {
        "date": "20150501",
        "total": 7.24
        }]
    }]
};

// format data
var data = [];
apiData.roles.forEach(function(d) {
	var keys = Object.keys(d);
  keys.forEach(function(k) {
    data.push({key: k, values: d[k]});
  })
});
//console.log(data);

        let svg = d3.select('div#chart').append('svg')
            .attr("width",960)
            .attr("height",500);

        let margin = {top: 20, right: 80, bottom: 30, left: 50},
            width = svg.attr("width") - margin.left - margin.right,
            height = svg.attr("height") - margin.top - margin.bottom,
            g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        let parseTime = d3.timeParse("%Y%m%d");

        let x = d3.scaleTime().range([0, width]),
            y = d3.scaleLinear().range([height, 0]),
            z = d3.scaleOrdinal(d3.schemeCategory10);

        let line = d3.line()
            .curve(d3.curveBasis)
            .x(function(d) { 
            	return x(parseTime(d.date)); 
            })
            .y(function(d) { return y(d.total); });





// ----- PASS apiData RESPONSE OBJECT INTO HERE ------
if (apiData) {
// console.log( apiData )
  
            x.domain([
                d3.min( data, function(c) { return d3.min(c.values, function(d) { return parseTime(d.date); }); }),
                d3.max( data, function(c) { return d3.max(c.values, function(d) { return parseTime(d.date); }); })
            ]);
           // console.log(x.domain());
            y.domain([
                d3.min( data, function(c) { return d3.min(c.values, function(d) { return +d.total; }); }),
                d3.max( data, function(c) { return d3.max(c.values, function(d) { return +d.total; }); }),
            ]);
//console.log(y.domain());

            z.domain( data.map(function(d) { return d.key; }) );

/*             g.append("text").attr("y",height/3).attr("x", width/3).text( ">>>" + apiData.roles.map( function(d) { d }).map(function (e) { return e } ).map(function (f) { return f } ) + "<<<" ) ; */
        // ----Everything below here is 'stock' Bostock D3 code

        g.append("g")
            .attr("class", "axis axis--x")
            .attr("transform", "translate(0,"+ height +")")
            .call(d3.axisBottom(x));

        g.append("g")
            .attr("class", "axis axis--y")
            .call(d3.axisLeft(y))
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", "0.71em")
            .attr("fill", "#000")
            .text("Total resource");

        let city = g.selectAll(".city")
            .data(data)
            .enter().append("g")
            .attr("class", "city");

        city.append("path")
            .attr("class", "line")
            .attr("d", function(d) { return line(d.values); })
            .style("stroke", function(d) { return z(d.key); });

/*         city.append("text")
            .datum(function(d) { return {id: d.key, value: d.values[d.values.length - 1]}; })
            .attr("transform", function(r) { 
              return function(d) { return "translate(" + x(d.date) + "," + y(d.total) + ")"; } })
            .attr("x", 3)
            .attr("dy", "0.35em")
            .style("font", "10px sans-serif")
            .text(function(d) { return d.key; }); */

        } else {
            g.append("text").attr("y",height/2).attr("x", width/2).text( "No API DATA!!!" );
        }
.city path {
  fill: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

<div id="chart">

</div>

希望这可以帮助。 :)

© www.soinside.com 2019 - 2024. All rights reserved.