【发布时间】:2017-06-10 02:46:04
【问题描述】:
这是一个奇怪的问题。它也有点长,所以提前道歉。 更新 - 它最终成为 2 个问题,请参阅下面的答案。
这是我的错误:EXCEPTION: this.svg.selectAll(...).data(...).enter is not a function
我有一个 angular-cli 客户端和一个节点 api 服务器。我可以使用 observable(下面的代码)从服务中检索 states.json 文件。 d3 喜欢该文件并显示预期的美国地图。
当我将 api 服务器中的服务目标从文件更改为 bluemix-cloudant 服务器时,我在客户端中收到上述错误。
当我使用 ngOnInit 在变体中 console.log 输出时,最初 mapData 打印为空数组并引发错误。这是错误的明显来源,因为没有数据,但是Chrome 调试器显示 get 请求待处理。请求完成后,控制台中会按预期打印数据。
- angular-cli 版本 1.0.0-beta.26
- 角度版本 ^2.3.1
- d3 版本 ^4.4.4
- rxjs 版本 ^5.0.1
map.component.ts:
import { Component, ElementRef, Input } from '@angular/core';
import * as D3 from 'd3';
import '../rxjs-operators';
import { MapService } from '../map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (private _element: ElementRef, private _mapService: MapService) {
this.host = D3.select(this._element.nativeElement);
this.getMapData();
this.setup();
this.buildSVG();
}
getMapData() {
this._mapService.getMapData()
.subscribe(
mapData => this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
setup() {
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
this.mapData = mapData;
this.projection = D3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = D3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
}
map.service.ts:
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class MapService {
private url = 'http://localhost:3000/api/mapData';
private socket;
constructor (private _http: Http) { }
getMapData(): Observable<any> {
return this._http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
这是一个异步的函数并且对数据的调用对于 d3 来说需要太长时间吗?
我希望这个问题 Uncaught TypeError: canvas.selectAll(...).data(...).enter is not a function in d3 会提供一些见解,但我没有看到任何见解。
非常感谢任何帮助或见解!
编辑: 以下是 Chrome 每个 Marks 请求的标头部分的屏幕截图。响应选项卡显示正确作为 GeoJSON 对象出现的数据。我还将该响应复制到本地文件中,并将其用作具有积极结果的地图源。
到目前为止的数据测试:GeoJSON 文件 (2.1mb)
- 本地文件,本地服务器:成功(响应时间 54ms)
- 同一个文件,远程服务器:数据返回浏览器之前的 D3 错误(750 毫秒)
- 来自远程服务器的 API 调用:数据返回浏览器之前的 D3 错误(2.1 秒)
【问题讨论】:
-
你能显示记录
mapData.features的输出吗? -
@Assan - 定义各州边界的是 GeoJSON 数据的一部分。这是一个示例:
"features": [ { "type": "Feature", "properties": { "GEO_ID": "0400000US01", "STATE": "01", "NAME": "Alabama", "LSAD": "", "CENSUSAREA": 50645.326000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -88.124658, 30.283640 ], [ -88.086812, 30.259864 ], [ -88.074854, 30.249119 ], [ -88.075856, 30.246139 ], [ -88.078786, 30.245039 ], ...},{next state and so on...}] -
您确定
this.mapData.features是您在setMap函数中所期望的(必须是一个数组)吗? -
@Assan 与节点服务器上文件的内容完全相同。正如我所说,d3 喜欢该文件并显示地图。 this.mapData.features 是 d3 生成地图所需的数组。
-
setMap()会被调用两次吗?一次是空数组,一次是预期数组?
标签: angular d3.js ibm-cloud rxjs cloudant