D3.js 和 Ionic 2:在第二个页面视图中不在 DOM 中的元素
D3.js and Ionic 2: Elements not in DOM on second page view
我创建了一个简单的 Ionic 应用程序来测试 d3.js 的集成。在这里能找到它:
https://github.com/wberger/ionic2-d3js-test
我是这样整合的:
- 创建一个带有侧边导航和单页的应用程序(
home.html
)
- 将
home.html
添加到菜单。
- 在 index.html 中加载 d3.js:
<script src="https://d3js.org/d3.v4.min.js"></script>
- 将
<div id="chart"></div>
添加到 home.html
- 在
home.ts
中创建 ngAfterViewInit()
中的图表
home
模板定义如下(source):
<ion-header>
<ion-navbar>
<button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
The world is your oyster.
<p>
If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will be your guide.
</p>
<div id="chart"></div>
<p>After</p>
</ion-content>
图表创建实现如下(source):
ngAfterViewInit() {
this.createChart();
}
createChart() {
var chart = d3.select("#chart").append("svg")
.attr("width", 100)
.attr("height", 100)
.append("g");
var rows = [
{x: 1, y: 1},
{x: 2, y: 2},
{x: 3, y: 3},
];
var xScale = d3.scaleLinear()
.range([0, 100])
.domain([1, 3]);
var yScale = d3.scaleLinear()
.range([100, 0])
.domain([1, 3]);
chart.selectAll(".dot")
.data(rows)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", (row) => { return xScale(row.x) })
.attr("cy", (row) => { return yScale(row.y) })
.attr("r", 3.5);
}
启动应用程序时,图表会按预期显示。但是当(重新)从侧面菜单打开页面时,图表不存在。在检查 Google Chrome 中的 TS/DOM 时,我可以在重新打开时观察到以下行为:
ngAfterViewInit()
被调用并依次调用 'createChart()'
- 之前创建的 SVG 元素仍然存在
- 第二个 SVG 元素由
createChart()
创建并填充
ngAfterViewInit()
之后创建的 SVG 消失了。似乎 DOM 被替换为空的(或缓存的??)模板。
所以我的问题是:
- 这里出了什么问题?我应该知道什么?
- 我该如何解决?
我通过将图表构建为一个组件解决了这个问题:
@Component({
selector: 'my-chart',
inputs: ['options', 'data'],
template: ``
})
export class MyChart implements OnChanges {
el: any;
options: MyChartOptions;
data: MyChartData;
chart: any;
arcMap: any;
/**
* Initializes the component with the root element.
*/
constructor(@Inject(ElementRef) elementRef: ElementRef) {
this.el = elementRef.nativeElement;
}
/**
* Handler for binding changes.
* @param changes The changed bound values.
*/
ngOnChanges(changes) {
if (changes.options) {
this.updateWithOptions(this.options);
}
if (changes.data) {
this.updateWithData(this.data);
}
}
// handlers, drawing etc. ...
}
然后可以整合如下:
<my-chart [data]="chartData" [options]="chartOptions"></my-chart>
不要忘记在app.module.ts
中注册组件,例如:
import { MyChart } from '../components/charts/my-chart';
// ...
@NgModule({
declarations: [
// ...
MyChart
],
imports: [
// ...
],
bootstrap: [MyApp],
entryComponents: [
// ...
],
providers: [
// ...
]
})
我创建了一个简单的 Ionic 应用程序来测试 d3.js 的集成。在这里能找到它: https://github.com/wberger/ionic2-d3js-test
我是这样整合的:
- 创建一个带有侧边导航和单页的应用程序(
home.html
) - 将
home.html
添加到菜单。 - 在 index.html 中加载 d3.js:
<script src="https://d3js.org/d3.v4.min.js"></script>
- 将
<div id="chart"></div>
添加到home.html
- 在
home.ts
中创建
ngAfterViewInit()
中的图表
home
模板定义如下(source):
<ion-header>
<ion-navbar>
<button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
The world is your oyster.
<p>
If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will be your guide.
</p>
<div id="chart"></div>
<p>After</p>
</ion-content>
图表创建实现如下(source):
ngAfterViewInit() {
this.createChart();
}
createChart() {
var chart = d3.select("#chart").append("svg")
.attr("width", 100)
.attr("height", 100)
.append("g");
var rows = [
{x: 1, y: 1},
{x: 2, y: 2},
{x: 3, y: 3},
];
var xScale = d3.scaleLinear()
.range([0, 100])
.domain([1, 3]);
var yScale = d3.scaleLinear()
.range([100, 0])
.domain([1, 3]);
chart.selectAll(".dot")
.data(rows)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", (row) => { return xScale(row.x) })
.attr("cy", (row) => { return yScale(row.y) })
.attr("r", 3.5);
}
启动应用程序时,图表会按预期显示。但是当(重新)从侧面菜单打开页面时,图表不存在。在检查 Google Chrome 中的 TS/DOM 时,我可以在重新打开时观察到以下行为:
ngAfterViewInit()
被调用并依次调用 'createChart()'- 之前创建的 SVG 元素仍然存在
- 第二个 SVG 元素由
createChart()
创建并填充
ngAfterViewInit()
之后创建的 SVG 消失了。似乎 DOM 被替换为空的(或缓存的??)模板。
所以我的问题是:
- 这里出了什么问题?我应该知道什么?
- 我该如何解决?
我通过将图表构建为一个组件解决了这个问题:
@Component({
selector: 'my-chart',
inputs: ['options', 'data'],
template: ``
})
export class MyChart implements OnChanges {
el: any;
options: MyChartOptions;
data: MyChartData;
chart: any;
arcMap: any;
/**
* Initializes the component with the root element.
*/
constructor(@Inject(ElementRef) elementRef: ElementRef) {
this.el = elementRef.nativeElement;
}
/**
* Handler for binding changes.
* @param changes The changed bound values.
*/
ngOnChanges(changes) {
if (changes.options) {
this.updateWithOptions(this.options);
}
if (changes.data) {
this.updateWithData(this.data);
}
}
// handlers, drawing etc. ...
}
然后可以整合如下:
<my-chart [data]="chartData" [options]="chartOptions"></my-chart>
不要忘记在app.module.ts
中注册组件,例如:
import { MyChart } from '../components/charts/my-chart';
// ...
@NgModule({
declarations: [
// ...
MyChart
],
imports: [
// ...
],
bootstrap: [MyApp],
entryComponents: [
// ...
],
providers: [
// ...
]
})