如何通过事件监听器正确传递信息?

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

我有一个 Flask/javascript Web 应用程序,但在通过事件侦听器传递信息时遇到问题。

这是我的代码:

fetch('/get_data')
  .then(response => response.json())
  .then(data => {
    const toggleValue1 = document.getElementById("toggleValue1");
    const toggleValue2 = document.getElementById("toggleValue2");

    // Initial render with both values displayed
    renderChart(data, toggleValue1.checked, toggleValue2.checked);

    // Update chart when checkboxes change
    toggleValue1.addEventListener("change", () => renderChart(data, toggleValue1.checked, toggleValue2.checked));
    toggleValue2.addEventListener("change", () => renderChart(data, toggleValue1.checked, toggleValue2.checked));
  })
  .catch(error => console.error("Error fetching data:", error));

/get_data
是一个烧瓶端点,定义如下:

@app.route('/get_data')
def timeseries():
    # Sample time series data
    data = [
        {"date": "2024-01-01", "value1": 10, "value2": 15},
        {"date": "2024-02-01", "value1": 20, "value2": 10},
        {"date": "2024-03-01", "value1": 15, "value2": 20},
        {"date": "2024-04-01", "value1": 30, "value2": 25},
        {"date": "2024-05-01", "value1": 25, "value2": 30},
    ]
    return jsonify(data)

我打印出了

data
的值,发现在初次调用
renderChart()
时以及初次调用
renderChart()
之后,一切都是正确的。但是,在切换复选框并且事件侦听器调用
renderChart()
后,
data
的日期为空值,如下所示:

[
        {"date": null, "value1": 10, "value2": 15},
        {"date": null, "value1": 20, "value2": 10},
        {"date": null, "value1": 15, "value2": 20},
        {"date": null, "value1": 30, "value2": 25},
        {"date": null, "value1": 25, "value2": 30},
    ]

我不明白为什么日期被变成空。有谁知道为什么会这样?

这是我的完整代码:

可视化_app.py

from flask import Flask, render_template, jsonify

app = Flask(__name__)

@app.route('/data_visualizations', methods=["GET", "POST"])
def get_data_visualizations():
    return render_template('data_visualizations.html')


@app.route('/get_data')
def timeseries():
    # Sample time series data
    data = [
        {"date": "2024-01-01", "value1": 10, "value2": 15},
        {"date": "2024-02-01", "value1": 20, "value2": 10},
        {"date": "2024-03-01", "value1": 15, "value2": 20},
        {"date": "2024-04-01", "value1": 30, "value2": 25},
        {"date": "2024-05-01", "value1": 25, "value2": 30},
    ]
    return jsonify(data)

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=True)
    #app.run(ssl_context=('cert.pem', 'key.pem'))

脚本.js

// Chart area and margins
const margin = { top: 20, right: 30, bottom: 50, left: 60 };
const width = 800 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;

const svg = d3.select("#chart")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left},${margin.top})`);

const color = { value1: "blue", value2: "orange" };

// Scales and axes
const x = d3.scaleTime().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);

const xAxisGroup = svg.append("g")
  .attr("class", "x-axis")
  .attr("transform", `translate(0,${height})`);

const yAxisGroup = svg.append("g")
  .attr("class", "y-axis");

// Line generators
const lineValue1 = d3.line()
  .x(d => x(d.date))
  .y(d => y(d.value1));

const lineValue2 = d3.line()
  .x(d => x(d.date))
  .y(d => y(d.value2));

// Function to render the chart
function renderChart(data, displayValue1, displayValue2) {

  console.log("Data 1: ", data)

  // Parse dates and set up data bounds
  const parseTime = d3.timeParse("%Y-%m-%d");
  data.forEach(d => {
    d.date = parseTime(d.date);
    d.value1 = +d.value1;
    d.value2 = +d.value2;
  });

  console.log("Data 2: ", data)

  // Set domains for scales
  x.domain(d3.extent(data, d => d.date));
  y.domain([0, d3.max(data, d => Math.max(d.value1, d.value2))]);

  // Draw the axes
  xAxisGroup.call(d3.axisBottom(x));
  yAxisGroup.call(d3.axisLeft(y));

  // Clear existing lines
  svg.selectAll(".line-value1").remove();
  svg.selectAll(".line-value2").remove();

  // Draw lines based on checkbox selections
  if (displayValue1) {
    svg.append("path")
      .datum(data)
      .attr("fill", "none")
      .attr("stroke", color.value1)
      .attr("stroke-width", 2)
      .attr("class", "line-value1")
      .attr("d", lineValue1);
  }

  if (displayValue2) {
    svg.append("path")
      .datum(data)
      .attr("fill", "none")
      .attr("stroke", color.value2)
      .attr("stroke-width", 2)
      .attr("class", "line-value2")
      .attr("d", lineValue2);
  }

  console.log("Data 3: ", data)
}

// Apply zoom behavior to the SVG container
svg.on(".zoom", null);

// Fetch data from the Flask API and initialize the chart
fetch('/get_data')
  .then(response => response.json())
  .then(data => {
    const toggleValue1 = document.getElementById("toggleValue1");
    const toggleValue2 = document.getElementById("toggleValue2");

    // Initial render with both values displayed
    renderChart(data, toggleValue1.checked, toggleValue2.checked);

    console.log("Data 4: ", data)

    // Update chart when checkboxes change
    toggleValue1.addEventListener("change", () => renderChart(data, toggleValue1.checked, toggleValue2.checked));
    toggleValue2.addEventListener("change", () => renderChart(data, toggleValue1.checked, toggleValue2.checked));
  })
  .catch(error => console.error("Error fetching data:", error));

数据可视化.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Time Series Line Graph with Toggle</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
  <div class="container">
    <h2>Time Series Data</h2>
    <div class="controls">
      <label><input type="checkbox" id="toggleValue1" checked> Value 1</label>
      <label><input type="checkbox" id="toggleValue2" checked> Value 2</label>
    </div>
    <div id="chart"></div>
  </div>

  <script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>
javascript python flask
1个回答
0
投票

我认为您的

script.js
文件中发生了错误。

在函数

renderChart
上,您使用
data
for 循环,但更改了原始源
data
,因此当您切换复选框时,参数
data
已被循环中先前的操作更改(因此
parseTime
方法失败了
null
)。

解决方案:

// by destructure the array
toggleValue1.addEventListener("change", () => renderChart([...data], toggleValue1.checked, toggleValue2.checked));
toggleValue2.addEventListener("change", () => renderChart([...data], toggleValue1.checked, toggleValue2.checked));

// or not modify the origin data
function renderChart(data, displayValue1, displayValue2) {
  const _data = [...data];

  console.log("Data 1: ", _data)

  // Parse dates and set up data bounds
  const parseTime = d3.timeParse("%Y-%m-%d");
  _data.forEach(d => {
    d.date = parseTime(d.date);
    d.value1 = +d.value1;
    d.value2 = +d.value2;
  });
  // replace all data to _data
  ...
}
© www.soinside.com 2019 - 2024. All rights reserved.