Angular项目API调用后变量未定义及多端异常问题排查
Angular项目问题修复方案
问题清单
- Chrome控制台无日志输出,Edge可正常打印
- 项目启动时,
ngOnInit读取CSV文件出现ERROR TypeError: Cannot read properties of undefined (reading 'trim') - 点击股票"View"按钮后图表显示,但报错
loader.js:98 ERROR Error: Uncaught (in promise): TypeError: Cannot use 'in' operator to search for 'Time Series (5min)' in undefined,且chart.component.ts中console.log("data", this.data);执行3次 - 外汇模块存在与上述第三点相同的异常
逐个问题修复
1. Chrome控制台无日志输出
- 检查Chrome控制台过滤设置:确保未勾选"Hide network"、"Warnings"或"Errors",根据需求调整"Preserve log"状态
- 取消代码中被注释的
console.log语句,比如app.component.ts内generate_stock_chart、symbol_search方法里的注释日志
2. CSV读取时trim报错
原因:CSV文件可能存在空行,拆分后cols[1]为undefined,调用trim()触发错误。
修改app.component.ts的ngOnInit方法:
ngOnInit(){ this.http.get('assets/csv/digital_currency_list.csv', {responseType: 'text'}).subscribe(data => { const rows = data.split('\n').filter(row => row.trim() !== ''); // 过滤空行 rows.shift(); this.crypto_list = rows.map((row : any) => { const cols = row.split(','); return { code: cols[0]?.trim() || '', // 可选链避免undefined name: cols[1]?.trim() || '' }; }); }); this.http.get('assets/csv/physical_currency_list.csv', {responseType: 'text'}).subscribe(data => { const rows = data.split('\n').filter(row => row.trim() !== ''); // 过滤空行 rows.shift(); rows.forEach((row : any) => { const cols = row.split(','); this.forex_list.push({ code: cols[0]?.trim() || '', name: cols[1]?.trim() || '' }); }); console.log("forex list", this.forex_list); }); }
3. Chart组件提前接收数据导致未定义错误(含外汇模块)
原因:
- Chart组件初始化时立即调用
drawChart,此时@Input数据尚未从API返回,导致undefined app.component.html渲染了两个app-chart实例,加上数据更新时的重绘,导致日志多次输出- 外汇模块的两个API请求异步执行,可能先触发
show_chart但数据未完全加载
修复步骤:
(1)优化模板渲染,合并Chart组件实例
修改app.component.html的chart渲染部分:
<div *ngIf="show_chart"> <app-chart [stockData]="chartData" [forexInfo]="currency_info"></app-chart> </div>
(2)修改Chart组件,监听数据变化并确保有效再绘制
更新chart.component.ts:
import { Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core'; declare var google: any; @Component({ selector: 'app-chart', templateUrl: './chart.component.html', styleUrls: ['./chart.component.css'] }) export class ChartComponent implements OnInit, OnChanges { @Input() stockData: any; @Input() forexInfo: any; chart_name = ''; timeSeriesData: any[] = []; forex_info = ''; ngOnInit(): void { google.charts.load('current', { packages: ['corechart'] }); google.charts.setOnLoadCallback(() => this.drawChart()); } ngOnChanges(changes: SimpleChanges): void { // 数据更新时重新绘制图表 if ((changes['stockData'] && changes['stockData'].currentValue) || (changes['forexInfo'] && changes['forexInfo'].currentValue)) { if (google.visualization) { this.drawChart(); } } } drawChart(): void { // 无有效数据时终止执行 if (!this.stockData && !this.forexInfo) return; const dataTable = new google.visualization.DataTable(); dataTable.addColumn('string', 'Time'); dataTable.addColumn('number', 'Open'); dataTable.addColumn('number', 'High'); dataTable.addColumn('number', 'Low'); dataTable.addColumn('number', 'Close'); const timeSeries = this.parseChartData(); if (timeSeries.length > 0) { dataTable.addRows(timeSeries); } const chartOptions = { title: this.chart_name, height: 500, width: 900, legend: { position: 'bottom' } }; const chartContainer = document.getElementById('chartContainer'); if (chartContainer) { const chart = new google.visualization.CandlestickChart(chartContainer); chart.draw(dataTable, chartOptions); } } parseChartData(): any[] { const timeSeries = []; this.timeSeriesData = []; this.chart_name = ''; this.forex_info = ''; // 处理股票数据 if (this.stockData && 'Time Series (5min)' in this.stockData) { this.chart_name = `${this.stockData["Meta Data"]["2. Symbol"]} stock price`; this.timeSeriesData = this.stockData['Time Series (5min)']; } // 处理外汇数据 else if (this.stockData && 'Time Series FX (Daily)' in this.stockData && this.forexInfo) { const meta = this.stockData["Meta Data"]; const exchangeRate = this.forexInfo["Realtime Currency Exchange Rate"]; this.forex_info = `1 ${meta["2. From Symbol"]} = ${exchangeRate["5. Exchange Rate"]} ${exchangeRate["4. To Currency Code"]} (Last refreshed: ${exchangeRate["6. Last Refreshed"]} ${exchangeRate["7. Time Zone"]})`; this.chart_name = `${meta["2. From Symbol"]} To ${meta["3. To Symbol"]}`; this.timeSeriesData = this.stockData['Time Series FX (Daily)']; } // 处理错误情况 else if (this.stockData && 'Error Message' in this.stockData) { this.forex_info = "Not available! Please choose another option"; return timeSeries; } // 转换时间序列数据 for (const key in this.timeSeriesData) { if (this.timeSeriesData.hasOwnProperty(key)) { const entry = this.timeSeriesData[key]; timeSeries.push([ new Date(key).toISOString(), parseFloat(entry['1. open']), parseFloat(entry['2. high']), parseFloat(entry['3. low']), parseFloat(entry['4. close']) ]); } } return timeSeries; } }
(3)优化API请求逻辑,确保数据完整后再显示图表
修改app.component.ts的generate_stock_chart和generate_forex_chart方法:
import { forkJoin } from 'rxjs'; // ... 其他代码 generate_stock_chart(){ this.url = `${this.starter_url}TIME_SERIES_INTRADAY&symbol=${this.stock_symbol}&interval=5min&apikey=${this.api_key}`; this.http.get(this.url).subscribe((data: any) => { if (data['Error Message']) { console.error(data['Error Message']); return; } this.chartData = data; this.show_chart = true; // 数据有效再显示图表 }, error => { console.error('API请求失败:', error); }); } generate_forex_chart() { const chart_url = `${this.starter_url}FX_DAILY&from_symbol=${this.from_currency}&to_symbol=${this.to_currency}&apikey=${this.api_key}`; const exchange_rate_url = `${this.starter_url}CURRENCY_EXCHANGE_RATE&from_currency=${this.from_currency}&to_symbol=${this.to_currency}&apikey=${this.api_key}`; // 等待两个API请求都完成后再更新数据和显示图表 forkJoin([ this.http.get(chart_url), this.http.get(exchange_rate_url) ]).subscribe(([chartData, currencyInfo]: any[]) => { if (chartData['Error Message'] || currencyInfo['Error Message']) { console.error('API请求错误:', chartData['Error Message'] || currencyInfo['Error Message']); return; } this.chartData = chartData; this.currency_info = currencyInfo; this.show_chart = true; }, error => { console.error('API请求失败:', error); }); }
内容的提问来源于stack exchange,提问作者bryan lee
相关产品推荐
相关产品推荐

