【问题标题】:TypeScript type confusion (TypeError: Cannot read property 'slice' of undefined)TypeScript 类型混淆(TypeError:无法读取未定义的属性“切片”)
【发布时间】:2021-09-26 23:13:09
【问题描述】:

我的 Angular 项目的 csv 文件中有以下数据,该文件还导入了 D3.js 库:

group,Nitrogen,normal,stress
banana,12,1,13
poacee,6,6,33
sorgho,11,28,12
triticum,19,6,1

我在打字稿文件中也有以下代码来显示堆叠条:

import { Component, OnInit } from '@angular/core';
import * as d3 from 'd3';

@Component({
  selector: 'app-stacked-bar',
  templateUrl: './stacked-bar.component.html',
  styleUrls: ['./stacked-bar.component.css']
})
export class StackedBarComponent implements OnInit {

  data = [
    {"group": "banana", "Nitrogen": "12", "normal": "1", "stress": "13"},
    {"group": "poacee", "Nitrogen": "6", "normal": "6", "stress": "33"},
    {"group": "sorgho", "Nitrogen": "11", "normal": "28", "stress": "12"},
    {"group": "triticum", "Nitrogen": "19", "normal": "6", "stress": "1"}
  ];

  svg: any;

  margin = 50;
  width = 750 - (this.margin * 2);
  height = 400 - (this.margin * 2);

  ngOnInit(): void {

    this.createSvg();
    this.drawBars(this.data);
  }

  createSvg(): void {

    this.svg = d3.select("figure#stacked-bar")
    .append("svg")
    .attr("width", this.width + (this.margin * 2))
    .attr("height", this.height + (this.margin * 2))
    .append("g")
    .attr("transform", "translate(" + this.margin + "," + this.margin + ")");
  }

  drawBars(data): void {
 
    // List of subgroups - Header of the csv file.
    // ["Nitrogen", "normal", "stress"]
    const subgroups = data.columns.slice(1);
            
    // List of groups - Value of the first column, called group.
    const groups = data.map(d => (d.group));

    // Create the X-axis band scale.
    const x = d3.scaleBand()
    .domain(groups)
    .range([0, this.width])
    //.domain(data.map(d => d.groups))
    .padding(0.2);

    // Draw the X-axis on the DOM.
    this.svg.append("g")
    .attr("transform", "translate(0," + this.height + ")")
    .call(d3.axisBottom(x).tickSizeOuter(0));
    
    // Create the Y-axis band scale.
    const y = d3.scaleLinear()
    .domain([0, 60])
    .range([this.height, 0]);

    // Draw the Y-axis on the DOM.
    this.svg.append("g")
    .call(d3.axisLeft(y));

    // color palette = one color per subgroup
    const color = d3.scaleOrdinal()
    .domain(subgroups)
    .range(['#e41a1c','#377eb8','#4daf4a']);

    // Stack the data per subgroup.
    const stackedData = d3.stack()
    .keys(subgroups)
    (data);

    // Create and fill the bars.
    this.svg.append("g")
    .selectAll("g")
    .data(stackedData)
    .join("g")
    .attr("fill", d => color(d.key))
    .selectAll("rect")    
    .data(d => d)
    .join("rect")
    .attr("x", d => x(d.data.group))
    .attr("y", d => y(d[1]))
    .attr("height", d => y(d[0]) - y(d[1]))
    .attr("width", x.bandwidth());
  }
}

但我只得到一个空的情节。当我检查页面时,我有这个控制台错误:

TypeError:无法读取未定义的属性“切片”

由于data 中的某些类型不明确,无法应用slice 方法,但是当我在代码中复制并粘贴["Nitrogen", "normal", "stress"] 代替subgroups 时,一切正常!

有人可以帮我解决这个问题吗?

附言图表应该是这样的:

【问题讨论】:

  • 如果是运行时错误,则与 TypeScript 类型无关。 TypeScript 类型是一个编译时的概念(除了枚举,它在运行时作为值存在)。
  • 你能看到这个运行时错误的原因吗?在我见过的所有示例中,d => y(d[0]) - y(d[1]) 返回一个数字并且它可以正常工作。在这里,它不返回一个数字...
  • 对于.attr("x"...,您使用d 作为对象;但是对于.attr("height"...,您使用d 作为数组。设置yheight时是否需要引用d的属性?
  • @Robin Mackenzie 我认为匿名函数(使用 d)基本上递归地减去每个条的高度,因此是一个数组。我编辑了我的问题并添加了堆叠条形图应该是什么样子的图像。您可以看到,例如,对于香蕉,我们需要一个长度为 12(红色)和 1(蓝色)和 13(绿色)的条形。请在我的帖子中查看新图像。我希望你能看到问题...
  • @CatarinaRuna - 很遗憾,我做不到,但这并不奇怪,因为我不使用 Angular 并且我没有使用过d3。 :-D 我会将简洁的箭头函数更改为d => { /*newline*/ const v1 = y(d[0]), v2 = y(d[1]), retVal = v1 - v2; /*newline*/ return retVal; /*newline*/},在return 上放置一个断点,然后查看v1v2retVal 的值。

标签: javascript angular typescript d3.js


【解决方案1】:

我现在无法自己测试这个,但是我从我在 TypeScript 中的编码中学到了很多类似的问题,通过将相关变量的类型设置为“任何”来解决,如下所示:

const subgroups = (data as any).columns.slice(1)

由于我对 Web 编程的探索时间很短,因此请谨慎对待,所以我不能说这种方法是否会对整体代码产生某种负面影响。欢迎对这个问题发表评论。

【讨论】:

    【解决方案2】:

    假设您正在使用此example,请注意它使用d3.csv 将 csv 输入转换为对象数组。 D3 创建这个数组加上一个自定义的columns 属性,该属性在示例中使用。

    const csv = `group,Nitrogen,normal,stress
    banana,12,1,13
    poacee,6,6,33
    sorgho,11,28,12
    triticum,19,6,1`;
    
    const data = d3.csvParse(csv);
    console.log(data.columns);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

    在您的代码中,您有相同的对象数组,但您没有显示 csv 导入方法,因此我们可能假设您没有使用 d3.csv 并使用其他方法来获取数据。

    在这种情况下,data.columns 的等效项是 Object.keys(data[0])。从那里你可以使用slice 来获取你想要为堆栈分组的列:

    const data = [
      {"group": "banana", "Nitrogen": "12", "normal": "1", "stress": "13"},
      {"group": "poacee", "Nitrogen": "6", "normal": "6", "stress": "33"},
      {"group": "sorgho", "Nitrogen": "11", "normal": "28", "stress": "12"},
      {"group": "triticum", "Nitrogen": "19", "normal": "6", "stress": "1"}
    ];
    
    const dataColumns = Object.keys(data[0]);
    console.log(dataColumns);
    
    const subgroups = dataColumns.slice(1)
    console.log(subgroups);
        

    【讨论】:

    • 祝福你!这就是我希望找到的。现在已经解决了。非常感谢。
    【解决方案3】:

    根据我对您的代码的理解,您的数据对象中没有属性列。

    您在文件开头定义了这样的数据:

    data = [
        {"group": "banana", "Nitrogen": "12", "normal": "1", "stress": "13"},
        {"group": "poacee", "Nitrogen": "6", "normal": "6", "stress": "33"},
        {"group": "sorgho", "Nitrogen": "11", "normal": "28", "stress": "12"},
        {"group": "triticum", "Nitrogen": "19", "normal": "6", "stress": "1"}
      ];
    

    所以我没有看到任何属性列。

    数据已经是一个数组,所以我认为你应该这样做:

    const subgroups = data.slice(1)

    而且,顺便说一句,您似乎对 groups 变量正确执行了此操作。

    【讨论】:

    • 谢谢。是的,它对groups 变量正确执行,但对于subgroups,它丢弃了第一个不是我想要的对象;我需要删除标题行group,Nitrogen,normal,stress
    【解决方案4】:

    你能试试下面的代码吗?

     const subgroups = data.columns?.slice(1) || [];
    

    【讨论】:

    • 谢谢,但它不起作用... slice 方法由于某种原因无法应用,但是当我在代码中复制并粘贴 ["Nitrogen", "normal", "stress"] 代替 subgroups 时,一切正常。如何从我的 data 中提取此 subgroups 数组?
    • 数据不包含您的代码中的子组被剪断。数据 = [ {“组”:“香蕉”,“氮”:“12”,“正常”:“1”,“压力”:“13”},{“组”:“poacee”,“氮”: “6”,“正常”:“6”,“压力”:“33”},{“组”:“sorgho”,“氮”:“11”,“正常”:“28”,“压力”: “12”},{“组”:“小麦”,“氮”:“19”,“正常”:“6”,“压力”:“1”}];
    • 你能告诉我对分组值的要求吗?
    • 让 allKeysForIndex0 = Object.keys(data[0]).splice(0); allKeysForIndex0.splice(0,1)
    猜你喜欢
    • 1970-01-01
    • 2018-07-18
    • 2018-03-13
    • 2021-11-20
    • 1970-01-01
    • 2021-10-31
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多